分享

用FPGA产生正弦信号

 奔跑的瓦力 2015-01-28




正弦信号,是一个模拟信号。而FPGA只能产生数字信号。因此需要用DA将数字量转化为模拟量。这里采用modelsim的模拟波形显示,就不需要DA模块了。产生正弦信号的方法有很多,这里用的是查找rom的方法,产生正弦信号。



正弦信号,是一个介于-11之间的模拟量。而正弦信号是周期变化的,因此这里只需要将半个正弦信号周期的值存进rom里,其余周期可以根据这个半个周期的值变化可得到。用matlab产生正弦信号的值。以0.01为步长,从0采集到pi/2。共158个点。



因此sin的值是小数,而FPGA是不能表示小数的,因此需要将小数扩大,以整数来表示。此次是以12位二进制来显示这整数。扩大的方法就是乘以2^12即可了。12位二进制数最大能表示2^12-1的值。这里用0表示最小值0,2^12-1表示最大值1.01中间的数,就可以扩大到用02^12-1中间的数来表示了。例如,0.3就可用整数0.3*2^12=1228.8,在向上取整,为1228.当然这样表示会有误差。



这里rom用的是ISEIP rom。将matlab生成的数据放进rom里面。然后依次读取rom的值,即可生成正弦信号了。



首先是生成rom的初始化文件。Xilinxrom的初始化文件的后缀为.coe。而这个特殊文件有固定的格式。






第一行的10表示下面的数字是10进制的。。。 后面的数据是依次存入rom的值,以逗号分开,最后一个以分号结束。前面两行的内容是固定的。



 



生成初始化文件的matlab如下所示:



fid = fopen('sin_rom.txt','w');



fprintf(fid,'MEMORY_INITIALIZATION_RADIX = 10;\n');



fprintf(fid,'MEMORY_INITIALIZATION_VECTOR =\n');



for i = 0:1:pi/2*100



    y = sin(i/100);



    rom =floor( y * 2^12);



    if i == 157



        fprintf(fid,'%d;',rom);



    else



        fprintf(fid,'%d,',rom);



    end



   



    if mod(i,10)==0 && i ~= 0



        fprintf(fid,'\n');



    end



end



fclose(fid);



生成的文件是.txt文件。将后缀直接改为.coe即可。然后复制到ISE分工程目录下。



 



初始化文件生成后,剩下就是编写verilog代码。



首先建一个工程,然后新建一个IP。找到rom,打开。设置按如下设置:



里是设置位宽,采用的是13位(第一位为符号位,后面12位为数据位)来显示sin的值。因此这里是设置为13.深度是因为要存158个值,所以这里设置为158.



里将刚刚生成的.coe文件载入,如果没有显示红色,就说明正确,否则错误。错误的原因是数据和设定的深度没有对齐。



 接下来直接生成就行了。



 



 接着就是编写verilog代码了。



首次查看用ip生成的romHDL例化代码.



sin_rom your_instance_name (



  .clka(clka), // input clka



  .addra(addra), // input [7 :
0] addra



  .douta(douta) // output [12
: 0] douta



);



从上面的程序可看出,只需要给时钟信号,和输入地址,就可以了。输出的就是正弦的数字信号了。分析正弦信号,前1/4个周期,地址从0自加,一直加到1571/4个周期的点数)。然后再自减,减到0。然后进入到负半周了。大家都知道,负数的表示是以二进制的补码来表示的,即绝对值数的二进制取反在加一。



因此要编写地址自加自减的代码,然后再根据地址的值,判断输出的值是正数,还是负数,负数的话,rom的输出值还要取反加一后再输出。



其代码,如下所示:



module sin_top(



    input clk,                        //输入时钟信号



        input [9:0] address,                //输入地址信号



        output reg [12:0] data_out          //输出sin的数字值



    );



 



  reg [7:0] add;           



  wire [12:0] douta;



  //  以下是判断地址的值



  always@ *  begin           



     if( address <= 157 )



            add = address;



        else if( address <= 315 )



            add = 10'd315 - address;



        else if( address <= 473 )



            add = address - 10'd316;



        else if( address <= 631 )



            add = 10'd631 - address;



        else



            add = 0;



  end



 



   



  //例化之前生成的sin_rom



  sin_rom u1_sin_rom (



  .clka(clk), // input clka



  .addra(add), // input [7 :
0] addra



  .douta(douta) // output [12
: 0] douta



  );



  // 判断输出值是正数还是负数。



  always@ *  begin



     if( address <= 315 )



            data_out = douta;



        else if( address <= 631 )



            data_out = ~douta + 1'b1;



        else



            data_out = 0;



  end



endmodule



 



 



程序写好了,剩下就是要仿真了。仿真的testbench,只需要输入时钟和地址信号就可以了。地址信号一直加一,直到不小于631,刚好一个周期结束。就返回0值,在继续自加。



测试代码如下所示:



module  sin_top_test;



       // Inputs



       reg clk;



       reg [9:0] address;



 



       // Outputs



       wire [12:0] data_out;



 



       // Instantiate the Unit
Under Test (UUT)



       sin_top uut (



                .clk(clk),



                .address(address),



                .data_out(data_out)



       );



      



       always#5  clk = ~clk;



 



       initial begin



                // Initialize
Inputs



                clk = 0;



                address = 0;



 



                // Wait 100 ns
for global reset to finish



                while(1)



          begin



             @(negedge(clk));



                             if( address < 631 )



                                 address = address + 1;



                             else



                                 address = 0;



          end               



       end



     



endmodule



 



   接着用modelsim仿真。因为有用到xilinxIP ROM。因此用modelsim单独仿真比较麻烦。因此这里是直接在ISE里面调用modelsim,这样比较简单。



只需在设置器件这里将仿真器设置为modelsim就可以了。



然后就可以直接仿真了。



modelsim里面



outformat的格式改为analog。这样一会显示的波形就会以模拟的形式显示了。



 



仿真后,标准的正弦波形就出来了。是不是很酷啊。。哈哈哈



这里采用的modelsim仿真,而不是用的是ISE自带的仿真,因为好像ISE的波形不能以模拟波形显示。



这样产生的正弦波,方法比较简单,但是不能调节,调节的话,就要重新生成rom的值。而且还伴随着有误差。可以增大位数,和减小采样间隔时间来减小误差。


   
   
   
   
   
   
  


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多