數字asic流程實驗(三) Verilog編寫&前仿真
1.Verilog編寫
本次實驗要實現的是一個三級抽取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}\)為輸入數據的位數,\(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
2.使用Modelsim進行前仿真
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編寫及前仿真的實驗流程。
3.參考資料
https://blog.csdn.net/FPGADesigner/article/details/80885415
《數字集成電路設計入門--從HDL到版圖》 於敦山 北大微電子學系