数字中频的基本原理和FPGA的实现.docx
《数字中频的基本原理和FPGA的实现.docx》由会员分享,可在线阅读,更多相关《数字中频的基本原理和FPGA的实现.docx(15页珍藏版)》请在冰豆网上搜索。
数字中频的基本原理和FPGA的实现
数字中频的基本原理和FPGA的实现
1.基本原理
数字中频主要分两部分,数字上变频(DUC)和数字下变频(DDC)。
它们的主要功能是相反,但原理和实现的方法是十分相似。
在R8905项目中由于采用了零中频技术,数字上变频和下变频有一些差别,数字上变频没有了NCO模块。
另外为了降低输出信号的峰均比又加入了削峰模块CFR,而CGC模块的引入则是补偿削峰所引起的功率损失。
图1数字上变频模块框图
在数字下变频中RSSI模块是信号的功率检测模块,它配合AGC电路将信号的输出功率稳定在一定范围内。
图2数字下变频模块框图
在DDC和DUC中主要使用3种滤波器分别是RRC,HB和CIC,它们个自有个自的特点。
RRC滤波器一般来讲阶数比较多,多用于低频处。
由于它的阶数比较多,所以可以得到比较锐利的带通特性,但它所用的乘法器比较多。
CIC滤波器不需要乘法器,但它的带内不是很平坦,适合用在高频处。
而HB滤波器的特性正好在它们之间,它有约一半的系数是0可以讲乘法器的个数减少一半。
削峰模块CFR实际上也是一组滤波器,它的功能是将CDMA信号中的峰值信号减小一些,以减小输出信号的峰均比,使射频功率放大器的效率更高。
削峰的模块框图如图3
图3单级削峰示意图
削峰的原理是这样的一个复信号(I,Q)如果它的模大于某个门限,就将其减去这个门限得到一个复信号(dI,dQ),否则(dI,dQ)=(0,0)。
将(dI,dQ)送到fir滤波器中,fir滤波器是一个低通滤波器将峰值限定在一定的带宽内,防止影响临道。
将原信号(I,Q)减去滤波后的信号(fir_i,fir_q)就得到了削峰的值。
如果有必要这这样的削峰可以连续做几次,在R8905设计中削峰用了两次。
2.滤波器的设计
由于在滤波的同时还有内插和抽取,所以充分利用这一特性可以减少FPGA使用的资源。
另外滤波器的系数一般都是对称的,可以将头和尾的数相加再乘滤波器的系数,这样可以大大减少乘法器的使用。
以R8905中的上变频RRC为例来说明:
设a(n)为RRC滤波器的系数而x(n)为3.84M输入数据则考虑了内插后的滤波器的数学表达式为
y=a(0)*x(n)+a
(1)*0+a
(2)*x(n-1)+.........+a(n-1)*0+a(0)*x(0)
=a(0)*(x(n)+x(0))+a
(2)*(x(n-1)+x
(1))......
其FPGA实现的逻辑框图如下
图4DUCRRC滤波器实现逻辑图
其中使用了4个乘法器和四个RAM以及一个ROM来存数据。
RRC_CTR_6144模块控制这些乘法器和ROM。
参考代码如下:
//////////////////////////////////////////
//////////////////////////////////////////
//Date:
SatJul2110:
51:
512007
//
//Author:
duanchenghong
//
//Company:
zte
//
//Description:
//RRC滤波器用了4个RAM和4个乘法器完成IQ两路的滤波功能,
//I,Q的处理方法完全相同。
数据同时写入4个RAM中,但读的地址不//同,由于RRC滤波器的系数是对称的所以读RAM的地址也是对称的,将地址
//对称的RAM读出数相加再和RRC滤波器系数相成再累加就可以得到最后的结果
//////////////////////////////////////////
//////////////////////////////////////////
modulerrc_ctr_6144(waddr,raddr0,raddr1,raddr2,raddr3,clk,reset,
data_en,dat0_out,dat1_out,dat2_out,dat3_out,coef,
raddr_coef,ih,il,qh,ql,coef_h,coef_l,mih,mil,mqh,mql,idat,qdat,rrc_en);
output[5:
0]waddr;reg[5:
0]waddr;
output[5:
0]raddr0;wire[5:
0]raddr0;
output[5:
0]raddr1;wire[5:
0]raddr1;
output[5:
0]raddr2;wire[5:
0]raddr2;
output[5:
0]raddr3;wire[5:
0]raddr3;
inputclk;wireclk;
inputreset;wirereset;
inputdata_en;wiredata_en;
input[31:
0]dat0_out;wire[31:
0]dat0_out;
input[31:
0]dat1_out;wire[31:
0]dat1_out;
input[31:
0]dat2_out;wire[31:
0]dat2_out;
input[31:
0]dat3_out;wire[31:
0]dat3_out;
input[31:
0]coef;wire[31:
0]coef;
output[3:
0]raddr_coef;wire[3:
0]raddr_coef;
output[16:
0]ih;reg[16:
0]ih;
output[16:
0]il;reg[16:
0]il;
output[16:
0]qh;reg[16:
0]qh;
output[16:
0]ql;reg[16:
0]ql;
output[15:
0]coef_h;wire[15:
0]coef_h;
output[15:
0]coef_l;wire[15:
0]coef_l;
input[32:
0]mih;wire[32:
0]mih;
input[32:
0]mil;wire[32:
0]mil;
input[32:
0]mqh;wire[32:
0]mqh;
input[32:
0]mql;wire[32:
0]mql;
output[15:
0]idat;reg[15:
0]idat;
output[15:
0]qdat;reg[15:
0]qdat;
outputrrc_en;regrrc_en;
assigncoef_h=coef[31:
16];
assigncoef_l=coef[15:
0];
reg[3:
0]cnt;
always@(posedgeclkornegedgereset)
if(~reset)
waddr<=0;
else
if(data_en)
waddr<=waddr+1'b1;
always@(posedgeclkornegedgereset)
if(~reset)
cnt<=0;
else
if(data_en)
cnt<=0;
else
cnt<=cnt+1'b1;
assignraddr_coef=cnt[3]?
cnt-3:
cnt-1'b1;
//系数的地址
assignraddr0=cnt[3]?
waddr-23+cnt[2:
0]:
waddr-23+cnt[2:
0]-1;
assignraddr1=cnt[3]?
waddr-23+cnt[2:
0]+6:
waddr-23+cnt[2:
0]+6-1;
assignraddr2=cnt[3]?
waddr-cnt[2:
0]-1:
waddr-cnt[2:
0]-1;
assignraddr3=cnt[3]?
waddr-cnt[2:
0]-6-1:
waddr-cnt[2:
0]-6-1;
//4个RAM的读地址
always@(posedgeclkornegedgereset)
if(~reset)
ih<=0;
else
ih<={dat0_out[31],dat0_out[31:
16]}+{dat2_out[31],dat2_out[31:
16]};
always@(posedgeclkornegedgereset)
if(~reset)
il<=0;
else
il<={dat1_out[31],dat1_out[31:
16]}+{dat3_out[31],dat3_out[31:
16]};
always@(posedgeclkornegedgereset)
if(~reset)
qh<=0;
else
qh<={dat0_out[15],dat0_out[15:
0]}+{dat2_out[15],dat2_out[15:
0]};
always@(posedgeclkornegedgereset)
if(~reset)
ql<=0;
else
ql<={dat1_out[15],dat1_out[15:
0]}+{dat3_out[15],dat3_out[15:
0]};
//为防止相加溢出,补一位
wire[35:
0]ex_mih,ex_mil,ex_mqh,ex_mql;
assignex_mih=mih[32]?
{3'b111,mih}:
{3'b000,mih};
assignex_mil=mil[32]?
{3'b111,mil}:
{3'b000,mil};
assignex_mqh=mqh[32]?
{3'b111,mqh}:
{3'b000,mqh};
assignex_mql=mql[32]?
{3'b111,mql}:
{3'b000,mql};
//为防止累加溢出,补3位
reg[35:
0]sum_ih,sum_il,sum_qh,sum_ql;
always@(posedgeclkornegedgereset)
if(~reset)
sum_ih<=0;
else
if(cnt[2:
0]==3'b001)
sum_ih<=0;
else
sum_ih<=sum_ih+ex_mih;
always@(posedgeclkornegedgereset)
if(~reset)
sum_il<=0;
else
if(cnt[2:
0]==3'b001)
sum_il<=0;
else
sum_il<=sum_il+ex_mil;
always@(posedgeclkornegedgereset)
if(~reset)
sum_qh<=0;
else
if(cnt[2:
0]==3'b001)
sum_qh<=0;
else
sum_qh<=sum_qh+ex_mqh;
always@(posedgeclkornegedgereset)
if(~reset)
sum_ql<=0;
else
if(cnt[2:
0]==3'b001)
sum_ql<=0;
else
sum_ql<=sum_ql+ex_mql;
always@(posedgeclkornegedgereset)
if(~reset)
rrc_en<=0;
else
if(cnt[2:
0]==3'b000)
rrc_en<=1'b1;
else
rrc_en<=0;
//产生RRC滤波器输出数据使能信号
always@(posedgeclkornegedgereset)
if(~reset)
idat<=0;
else
if(cnt[2:
0]==3'b000)
idat<=(((sum_ih+sum_il)>>13)+1'b1)>>1;
always@(posedgeclkornegedgereset)
if(~reset)
qdat<=0;
else
if(cnt[2:
0]==3'b000)
qdat<=(((sum_qh+sum_ql)>>13)+1'b1)>>1;
//产生I,Q两路数据并对末位进行四舍五入
endmodule
如果是半带滤波器,考虑到内插
在奇数拍时有
y=a(0)*x(n)+0*0+a
(2)*x(n-1)+.........+0*0+a(0)*x(0)
=a(0)*(x(n)+x(0))+a
(2)*(x(n-1)+x
(1))......
而在偶数拍有
y=a(0)*0+0*x(n-1)+a
(2)*0+....+a(m)x(m)+0...+0;
及只有中间项有数,其他各项不是系数为0,就是数据为0,且中间项的系数一般是0.5,利用这一点可以减少FPGA的资源。
HB滤波器的设计和RRC十分相似,如下图
图5HB滤波器设计框图
而CIC滤波器相对简单些,它实际上就是一些加法和一些减法,其设计框图如图6所示
图6CIC滤波器设计框图
在CIC滤波器中需要注意的是d1clk模块,它只是为了调整时序。
3.设计对时序要求的满足:
完成代码设计后除了要保证仿真正确同时也要保证电路的时序能够满足设计的要求。
从图1可以看到信号的频率随着每一次内插而变高,那各个滤波器的工作时钟是不是也要逐步提高呢?
实际上比较好的方法是时钟始终工作在最高频率61.44M上而用使能信号来标识数据。
当数据的速率是3.84M时就每16个61.44M时钟产生一个使能,而当速率是7.68M时就每8个61.44M时钟产生一个使能。
这样整个DUC电路只有一个时钟,便于时钟的时序分析。
如果我们使用Xilinx的ISE进行编译,建议综合工具使用synplifyPro而不要使用ISE本身的XST。
选用synplifyPro的方法如图7
图7在ISE中选用synplifyPro
在ISE的集成环境中有两个地方可以对时钟进行约束,分别是synplifyPro的sdc约束文件和ISE的UCF约束文件。
sdc文件主要通过改变代码生成的电路来提高速度,而UCF通过布局布线来提高速度,另外用面积约束的方法把相关模块放在一起减少布线延时也可以提高速度。
点击synplify界面中红线圈定的图标可以编辑时钟约束文件。
图8synplify中加约束文件
同样在ISE中也要加约束文件
图9在ISE中加约束文件
在ISE中可以添加时序约束和面积约束来提高时序。
添加约束的时候可以加入一些过约束,如果你希望时钟可以跑到10ns,可以将约束设为8~9ns。
但无论如何良好的代码设计是电路时序的根本保证。
如果时序不能满足,ISE的时序分析工具可以帮助你指出最长延时的路径,点击如图10所示分析关键可以看到关键路径。
图10ISE时序分析工具
ISE的时序分析工具清楚的告诉你那条路径时序有问题,如图11所示
图11路径延时的分析报告
4.怎样验证频域电路
滤波器电路设计一个最大的问题是它是针对频域的设计,算法又比较复杂而我们一般的仿真工具都是针对时域的。
怎么认定我们的设计是正确的往往是一个比较困难的事情。
有以下几种方法可以帮助判断。
1.输入一个等效幅度为1的冲击信号,滤波器的输出将是滤波器的各个系数
2.输入单音信号,分析输出信号的频谱是否只有一个单音频点。
3.和matlab仿真的数据逐个比较