FPGA设计技巧与案例详解读书笔记之波形发生器
0赞波形发生器
设计目标:使用FPGA与DAC实现一个频率可调相位可调的正弦/锯齿/三角/方波的信号发生器。
设计之初首先确定整体框图,这样在写分模块的比较清晰,也知道每个模块之间的连接方式。这里分成三个子模块,分别是按键输出、DDS以及DAC驱动。如下所示
一. 波形发生器之ROM
1.1波形数据生成
首先FPGA内部其实并没有真正的ROM,只有RAM,在RAM经过适当的配置可以当成ROM来用。此处ROM是为了存放波形数据,采用杭州康芯公司的Guagle_wave来生成mif(Memory Initialization File)数据。软件下载可以在论坛搜到前辈们共享的链接。软件界面如下:
首先在查看—全局参数中设置相应的参数,由于实验用的8为DAC因此在这里设置数据位宽8,其他数据可以对应调节。然后在设定波形处就可以选择想要的波形或者在手绘波形界面画任意波形。
此处应将mif文件放置于工程的prj目录下,不清楚为什么放在其他目录下仿真数据一直为0。
1.2 IP核 ROM设计及仿真
返回QII在Tools—Megawizard Plug-In Manger选择new一个,找到单端口的ROM将文件定位到ip文件夹下,命名好ip名称,最终的设置是8bits位宽,256words深度,单clock,无使能、清零,q输出寄存一下。
最终设计好的ip核如下所示,对每一种波形建立的单独的ROM数据:
将其中之一设计为顶层文件编译无误后写testbench:
`timescale 1ns/1ns `define clk_period 20 module rom_sawtooth_tb; reg [7:0] addr; reg clk; wire [7:0] q; rom_sawtooth rom_sawtooth( .address(addr), .clock(clk), .q(q) ); initial clk = 1'b0; always #(`clk_period/2) clk = ~clk; integer i; initial begin addr = 0; #2; for(i=0;i<2550;i=i+1) begin #(`clk_period) ; addr = i; //addr = addr + 1; end #(`clk_period*56); $stop; end endmodule
|
设置好仿真设置:
在modelsim中可以观察到波形数据如下:
在仿真时候可能会出现:
可以在q上右键properties—Format,将Max Min改为255 0,即可将波形调到正常范围显示。
通过仿真其他ROM同样可以观测到:
这样ROM的设计及仿真就算完成了。
二. 波形发生器之DDS
2.1 DDS原理直接数字频率合成技术(Direct Digital Synthesis,DDS) 是一种从相位概念出发直接合成所需要的波形的新的全数字频率合成技术,该技术具有频率分辨率高、频率变化速度快、相位可连续性变化等特点,在数字通信系统中被广泛采用,是信号生成的最佳选择。 DDS 由相位累加器、波形ROM、数模转换器(DAC)以及低通滤波器(LPF)组成。其基本原理就是将波形数据先存储起来,然后在频率控制字K的作用下,通过相位累加器从存储器中读出波形数据,最后经过D-A转换和低通滤波后输出合成频率信号。
DDS输出信号频率为,其中为参考书时钟,K为频率控制字,N为累加器位数。因此理论上由以上三个参数就可以得出任意的输出频率。由式子可以看出频率分辨率由参考时钟和累加器的位数决定,当参考时钟频率越高,累加器位数越高,输出频率分辨率就越高。
从32位累加器结果中提取高8位作为ROM的查询地址,因此产生的误差会对频谱纯度有影响,但是对波形的精度的影响是可以忽略的。
累加器的溢出频率也就是DDS输出的信号频率。
2.2 DDS的实现
DDS模块的编写如下:
///相位累加器 调节频率 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) fre_add <= 32'b0; else fre_add <= fre_add + F_word; end //ROM地址生成 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) rom_addr <= 8'd0; else rom_addr <= fre_add[31:24] + P_word; end //波形ROM选择 always@(posedge Clk or negedge Rst_n) begin if(!Rst_n) q <= 8'b0; else begin case(wave_select) 2'b00 : begin q <= q_sin; end //正弦波 2'b01 : begin q <= q_tri; end //三角波 2'b10 : begin q <= q_saw; end //锯齿波 2'b11 : begin q <= q_sqa; end //方波 default : begin q <= q_sin; end //正弦波 endcase end end //rom_ip rom_sin rom_sin( .address(rom_addr), .clock(Clk), .q(q_sin) );//正弦波 rom_triangle rom_triangle( .address(rom_addr), .clock(Clk), .q(q_tri) );//三角波 rom_sawtooth rom_sawtooth( .address(rom_addr), .clock(Clk), .q(q_saw) );//锯齿波 rom_sqare rom_sqare( .address(rom_addr), .clock(Clk), .q(q_sqa) );//方波
|
综合后部分RTL视图为:
可以看出与DDS的原理图一样,再经过以下仿真
`timescale 1ns/1ns `define clk_period 20 module DDS_tb; reg Clk; reg Rst_n; reg [1:0] wave_select; reg [31:0] F_word; reg [7:0] P_word; wire [7:0] q; initial Clk = 1'b0; always #(`clk_period/2) Clk =! Clk; initial begin Rst_n = 1'b0; F_word = 32'd0; P_word = 8'd0; #(`clk_period*20) Rst_n = 1'b1; F_word = 32'd5000; wave_select = 2'b00; #(`clk_period*5_000_000) wave_select = 2'b01; #(`clk_period*5_000_000) wave_select = 2'b10; #(`clk_period*5_000_000) wave_select = 2'b11; #(`clk_period*5_000_000) #(`clk_period*2000) $stop; end DDS DDS( .Clk(Clk), .Rst_n(Rst_n), .wave_select(wave_select), .F_word(F_word), .P_word(P_word), .q(q) ); endmodule |
可以看到以下图像,在切换不同的波形时可以看出输出波形数据正常。
三. 波形发生器之DAC
此处选用TLC560作为DA转换芯片。在用FPGA驱动此芯片基本就是线性序列机以及有限状态机的思想。
最后封装好的顶层RTL视图如下所示:
文件夹采用良好的命名规则,在设置仿真脚本或者以后再翻看一目了然。
附:“栗子”的工程以及本文word格式
链接:http://pan.baidu.com/s/1cmIxUu 密码:7uhi