基于cordic算法的数字下变频的实现---------孙月2014/12/11Matlab实现的参数采样频率fs=62e6;调制频率f1=15.5e6;采样点数N=1024*32;n=1:N;时域时间“t”t=n/fs;频域里的频率参考freq=(-N/2:N/2-1)/N*fs;在一个matlab脚本中,时域到频域转换之后。它们的点数是一一对应的,均为N个点。在时域中N个点,只是离散的点与时间“t”没有关系,作图时plot(t,x)才建立的关系,可以理解为把N个点放在了时间t上。频域上也是如此(一定要真正理解)。主要步骤1、用wgn函数产生N点的白噪声x;2、再将白噪声信号x,用2M的低通滤波器,产生零中频的带宽信号x_0;3、用调制信号cos(2*pi*fi.*t)与信号x_0相乘,变频到15.48M为中心的带宽信号sig。将信号sig扩大2^14倍后,以2进制补码的形式存入到txt文本中,作为硬件实现时的测试激励信号;4、在matlab中,用cordic算法实现将信号分为I,Q两路,并作时域,和频域分析。5、同时在matlab中直接调用cos,sin函数,产生NCO算法,验证4中的cordic算法是否正确。6、基于ise平台,实现cordic算法,并将I,Q两路输出存在txt文本中,再导入到matlab中,做频谱分析,与(4)中matlab实现的cordic算法结果比对,验证硬件实现是否正确。下面是具体实现,以及遇到的问题Wgn(1,N,1)是产生一维的N个点,平均功率为1.低通滤波器的实现有两种方式一、直接调用fdatool工具,设置参数,低通滤波时采样频率fs2fp。带通时,fs2(fh-fl),将fs设置的稍大一点性能更好。生成的系数,通过“file”—”export”导出,在matlab中保存以num_1.mat格式保存在该件夹中。在本脚本中使用时,fir_1=load('Num_1.mat'),fir_1.Num_1即为各系数。二、直接使用fir1(N,Wn)函数,直接生成一个N级的FIR数字低通滤波器,返回一个长度为N+1的向量Wn=fpass/(fs/2).让信号通过低通滤波器,可以直接引用filter函数,x_0=filter(coefficient,1,x),x_0为滤除高频成分的输出信号。X是输入信号。调制信号与信号x_0相乘,两个相量相乘可以采用点乘“.*”,变频到15.48M为中心的带宽信号sig。此处输出的信号sig,会作为硬件的测试输入数据,参与以后的算术运算,所以要以二进制补码的形式保存在一个txt文本里。这里又要用到一个函数fi,FI=fi(MAX,1,16)即把信号sig中最大的数MAX=1(由于此处做了归一化处理)用16位字长的二进制数表示,其中1,代表有一位是符号位,整数位一位,小数部分是14位,共16位。最后将sig归一化的结果*2^14后用round取整,得y(i),即保留到了sig信号的小数点后14位。此处还需要注意到是,负数y(i)转成补码时,将y(i)+2^16,再转换成二进制,求得二进制负数的补码2^16次方对于16位二进制是一个周期,如y=-5,-5的补码=1111_1111_1111_1011,5的补码=0000_0000_0000_0101,16位,2^16+(—5)=65531,65531的二进制=1111_1111_1111_1011.与-5的补码相同。在matlab中产生cordic算法Cordic算法有两种工作模式,一种是旋转模式,一种是向量模式。旋转模式是赋初值x(0)=1,y(0)=0,通过迭代使最后的旋转角度Zi趋于0.X(i)=cos(μ),y(i)=sin(u).而向量模式不断的使y(i)趋于0。本文采用的是旋转模式,sig(n)即为x(1),y(1)=0.sig每输入一个数据相位累加2*pi*f1/fs,经过cordic算法的n级循环后,输出I,Q两路信号。cos,sin运算以2*pi为周期,相位累加超过2*pi时,将其转换到[0:2*pi]域,即累加的角度angle_add-2*pi。此处不能用floor函数,原因未知????由于cordic算法i次旋转的角度,为atan(1/2^(-i+1)),N次旋转之后的累加旋转角度在(-99度,99度)之间。所以在进入到cordic算法的第一级时,要将[0:2*pi]域的累加相位在转换到第一象限,用xf,yf标记转换过程中第一级输入信号x(1),y(1)符号的变化。例如pi/2anglepi时,angle_rem(1)=pi-angle,xf=-1,yf=1。在旋转过程中,第i级的剩余角度如果是正的,那么第i+1级就逆时针旋转atan(1/2^(-i))度,第i+1级的剩余角度为angle_rem(i+1)=angle_rem(i)“---”atan(1/2^(-i);x(i+1)=x(i)-2^(-i+1)*y(i);y(i+1)=2^(-i+1)*x(i)+y(i)。第i级的剩余角度如果是负的,那么第i+1级就顺时针旋转atan(1/2^(-i))度,第i+1级的剩余角度为angle_rem(i+1)=angle_rem(i)+atan(1/2^(-i)总体思想是使角度越来越小,x(i+1)=x(i)+2^(-i+1)*y(i);y(i+1)=--2^(-i+1)*x(i)+y(i)。经过cordic算法的多级迭代之后,x,y会有一个常数增益k=1/0.60725,所以最后一级的结果乘以xf,yf再乘以0.60725作为最终信号的I,Q两路的输出。同时在matlab中直接调用cos,sin函数,产生NCO算法,验证matlab实现的cordic算法是否正确,如果不正确,逐一修改。-----------------------------------下面是基于ise平台的硬件实现部分---------------------------------------首先要将各个模拟值进行量化,其中2*pi用9位位宽1_0000_0000表示由于角度运算也是有“正”“负”之分,所以还要有一位符号位,最终用10位位宽基于2*pi=01_0000_0000来量化各个角度。因为二进制角度值00_0000_0001表示的精度是2*pi/2^8,所以cordic算法只能做六级,第七级的角度,用二进制值量化后为0各个运算值不再改变。输入的信号是16位位宽,但由于cordic迭代过程中存在增益,所以为防止溢出现象,各个迭代结果要用多一位的位宽17位位宽来表示。在把sig赋给第一级信号sig_0时,扩展的最高位是符号位sigi_0={sig[15],sig};不能直接sigi_0=sig(这样的结果是错的);实现过程采用了6级流水的方式实现,所以在迭代的过程中要确保信号的幅度值与相位值不要“错位”,正负标记xf,yf也要跟着流水的级数传递,xf_0到xf_6,最终的输出信号sig_I和sig_Q的符号由xf_6也就是(信号输进来时的xf_0的传递结果)和yf_6(信号输进来时的yf_0的传递结果)来决定。在迭代过程中的正负判断时,x为定义的signed数,不能使用x1’b0或x1’b0这种形式,结果出错,原因未知。可以采用以下两种方式进行判断(1)(x[mostbit]==1’b0&&(|(x[mostbit-1:0])==1’b1))表示大于零x[mostbit]==1’b1表示小于零。(2)定义signedparameterzero==16’h0000;xzero表示大于0.Xzero表示小于0.基于cordic算法的迭代过程中,*2^(-i)在硬件实现时是移位。移位也有2种方式实现。但将x[16:0]向右移2位,赋值给z[16:0]。Z[16:0]=(x2);这种写法是错误的,此时高位空出的两位系统会自动填0,如果x是负数,则z变成了正数,值不定。正确的方法是,用复制,拼接的方式,z[16:0]={{2{x[16]}},x[16:2]}复制倍数2外边也要有一个{}。在进行补码运算时减去一个数等于,加上一个数的反再加1。将输出的结果以十进制的方式存入到txt文本中,在matlab中时域和频域分析,验证算法的正确性。下面介绍一下通过文件读写的方式实现ISE和matlab的联合仿真。(详见文档)(通过文件读写方式实现Matlab和Modelsim的联合仿真)总体思想:现在MATLAB中产生仿真所需要的输入信号,以十六进制形式存放在数据文件中,在ISE中用verilog语言编写测试文件,做时序仿真,最后将结果存入另外一个数据文件,最后在matlab中将ISE的仿真输出文件读入一个数组中,以便可以作图分心,进一步做误差分析。1、Matlab产生数据用作Modelsim仿真在matlab脚本所在的文件夹中先新建一个data_in的txt文本fid=fopen('data_in.txt','wt');;先把文本打开,fid为文本标志符,“wt”文件以文本格式打开,删除已存在文件的内容,并以只写格式打开。fprintf(fid,'%x\n',x);fprintf把信号十六进制的形式写入到fid中。fclose(fid);把fid文件,关上。然后将产生的data_in.txt文件复制到ise的工程下,在verilog文件中先定义一个与要读入的数据位宽与长度相同的数组,然后通过$readmemh命令,将文件中的数据读入。reg[15:0]memory[0:32768];//定义一个16bit32768的数组initialbegin$readmemh(data_in.txt,memory);//将data_in.txt中的数据读入存储器memoryEnd再通过下面的代码把memory中的信号给输入sigalways@(posedgeclkornegedgerst_n)beginif(rst_n==0)sig=16'b0;elsesig=memory[i];endi是累加计数。always@(posedgeclkornegedgerst_n)beginif(rst_n==0)i=0;elseif(i==32767)i=0;elsei=i+1;end2.Matlab对ISE仿真生成的数据进行分析Matlab对Modelsim仿真生成数据的处理也是通过文件读写实现的。initialbeginI_file=$fopen(sig_I.txt);Endalways@(i)begin$fdisplay(I_file,%d,sig_I);if(i==32767)$stop;End下面再通过一段matlab代码将sig_I.txt中的数据读取进行分析fid_i=fopen('sig_I.txt','r');fori=1:Ninum(i)=fscanf(fid_i,'%d',1);endfclose(fid_i);fscanf(fid_i,'%d',1)这句话的意思是把fid_i中的数据以十进制的方式读出。当利用fscanf函数时要注意两点,第一:保证读取的数据格式和文件中保存的数据格式是相同的,例如这里文件中保存的格式是十进制,所以读取的时候也应该以十进制的形式读出。第二:要保证文件中数据的个数和设定的读取的数目(这里是32768)保持一致。例如,要将生成文件data_out.txt中多余的换行符去掉(一般最后会多出一行),否则Matlab会将空的行也当做一个数据,从而两个数目不一致,导致Matlab报错。滤波器参数设置