本次实验要实现的是一个三级抽取CIC滤波器,抽取系数为64。回顾上一章节中的CIC滤波器结构,可以发现其硬件实现是非常简单的,积分器的部分通过加法器与D触发器即可实现,降采样通过分频器实现,梳状器的部分则通过减法器和触发器实现。
编写分频器的verilog实现,其输入信号为时钟信号clk与复位信号rst_n,输出信号为64倍分频后的时钟信号clk_div。
分频器使用计数器实现。当复位信号为低时,计数器值复位为0,clk_div输出为0;当复位信号为高时,计数器在时钟clk的每个上升沿计数,当第32个上升时钟沿到来时clk_div进行翻转,由于计数器位数为5位,第32个上升时钟沿到来的同时计数器也清零了,直到下一次的第32个上升时钟沿再重复上述过程,故clk_div两次翻转的时间(一个周期)为64个clk周期,从而实现了64倍分频。
分频器的verilog代码实现如下:
module divider( input clk, input rst_n, output reg clk_div ); reg [4:0]count; always @(posedge clk or negedge rst_n) begin if (rst_n == 0) begin count <= 5'd0; clk_div <= 1'b0; end else if (count < 31) begin count <= count + 1; clk_div <= clk_div; end else begin count <= count + 1; clk_div <= ~clk_div; end end endmodule
编写CIC滤波器的verilog实现,其输入信号为时钟clk,复位信号rst_n以及调制器输入信号in,输出信号为量化值out。
由于积分运算会导致数据位宽变宽,需要通过公式计算
\[B_{out}=B_{in}+Nlog_2(RM) \]公式中,\(B_{out}\)为输出数据的位数,\(B_{in}\)为输入数据的位数,\(N\)为滤波器级数,\(R\)为滤波器阶数,\(M\)为降采样系数。代入数据后可以计算得到所需要的位数为19位,因此设计寄存器位宽为19。
需要注意的是梳状器作为降采样部分的后级,所有的时钟信号都需要使用分频器64倍分频后的时钟clk_div。
CIC滤波器的verilog实现代码如下:
module cic_filter( input clk, input rst_n, input in, output [18:0] out ); reg [18:0]out_reg; wire clk_div; reg [18:0]sum1,sum2,sum3; wire [18:0]sum1_nxt,sum2_nxt,sum3_nxt; // 积分器加法器部分 assign sum1_nxt = sum1 + in; assign sum2_nxt = sum2 + sum1; assign sum3_nxt = sum3 + sum2; // 积分器D触发器部分 always @(posedge clk or negedge rst_n) begin if (rst_n == 0) begin sum1 <= 19'b0; sum2 <= 19'b0; sum3 <= 19'b0; end else begin sum1 <= sum1_nxt; sum2 <= sum2_nxt; sum3 <= sum3_nxt; end end // 调用分频器 divider div( .clk(clk), .rst_n(rst_n), .clk_div(clk_div) ); reg [18:0]sub1,sub2,sub3; wire [18:0]sub1_nxt,sub2_nxt,sub3_nxt; // 梳状器减法器部分 assign sub1_nxt = sum3_nxt - sub1; assign sub2_nxt = sub1_nxt - sub2; assign sub3_nxt = sub2_nxt - sub3; // 梳状器D触发器部分 always @(posedge clk_div or negedge rst_n) begin if (rst_n == 0) begin sub1 <= 19'b0; sub2 <= 19'b0; sub3 <= 19'b0; end else begin sub1 <= sum3_nxt; sub2 <= sub1_nxt; sub3 <= sub2_nxt; end end // 输出 always @(posedge clk_div or negedge rst_n) begin if (rst_n == 0) begin out_reg <= 0; end else begin out_reg <= sub3_nxt; end end assign out = out_reg; endmodule
编写testbench用于仿真,文件1k1000mv.txt中存储了一段∑-Δ调制器对一个周期的正弦波采样后输出的码流,通过系统任务$readmemb将其读入声明好的存储器mem中,并将数据按次序以1位码流的形式进行输出到CIC滤波器的输入端口in,此外在testbench中设置时钟信号clk周期为156.25ns,复位信号rst_n在仿真开始500ns后由低电平变为高电平。
testbench的verilog实现代码如下:
`timescale 1ns/1ns `define period 78.125 module testbench; // input reg clk,rst_n,in; // output wire [18:0]out; // 设置时钟周期为156.25ns always #`period clk <= ~clk; // 初始化 initial begin rst_n <= 1'b0; clk <= 1'b0; #500; rst_n <= 1'b1; end integer i; // 定义存储器mem reg mem[0:3000000]; // 将1k1000mv.txt文件读入mem initial $readmemb("1k1000mv.txt",mem); // 将mem中数据次序输出到in always @(posedge clk or negedge rst_n) begin if(rst_n == 0) begin i = 0; in <= 0; end else begin in <= mem[i]; i = i + 1; end end // 调用cic滤波器 cic_filter cic( .clk(clk), .rst_n(rst_n), .in(in), .out(out) ); endmodule
1.打开Modelsim,File---->New---->Project.. 建立新工程,命名为CICFilter,并设置工程路径
2.将编写好的verilog文件、1k1000mv.txt全部放到工程路径下
3.在Modelsim的Project标签页中右键,点击Add to Project ---> Existing File... 选择工程路径下的verilog文件,Add file as type选为Verilog。将三个verilog文件全部添加进工程。
4.在Modelsim的Project标签页中右键,点击Compile ---> Compile All,编译全部verilog文件
5.若编译成功,下方Transcript窗口会输出编译成功的信息。若提示失败则根据报错信息修改verilog并再次编译。
6.点击Simulate--->Start Simulation... 在弹出的窗口中,点击work左侧的小加号展开,在展开的栏目中点击选中testbench,进入仿真环境。
7.在仿真环境中①为Instance窗口,所有例化的模块都会显示在其中;②为Objects窗口,选中①中的模块后,该窗口会对应显示模块中的信号,寄存器,存储器等;③为Wave窗口,用于显示仿真波形。在Instance窗口中点击testbench,在Objects窗口中选中out并右键,Add to ---> Wave ---> Selected Signals,将out信号加入Wave窗口
8.以同样的方式将in信号也添加进Wave窗口
9.修改右上角仿真时间,设置为1ms,再点击其右侧的run图标
10.此时Wave窗口应出现绿色波形,点击左侧小放大镜自动缩放波形时间轴
11.在Wave窗口中右键out信号,Format ---> Analog(automatic)
12.可以看到一个周期正弦波信号
13.多次点击run图标运行仿真,可以看到多个周期的正弦波
14.使用左侧放大镜功能调整波形横轴分度,可以看到在out值大的部分(波峰),in信号是以1为主的码流,在out值小的部分(波谷),in信号是以0为主的码流,和上一章节的调制器输出与CIC滤波器输出的关系是一致的。
至此完成了CIC滤波器的Verilog编写及前仿真的实验流程。
https://blog.csdn.net/FPGADesigner/article/details/80885415
《数字集成电路设计入门--从HDL到版图》 于敦山 北大微电子学系