verilog
digital design
邏輯設計
邏設
在寫完程式碼之後,勢必要測試它是否正確,而 testbench (簡稱 tb)就是用來幫助我們測試我們的程式是否有誤的方法。
testbench 基本上也是一個 verilog 檔案( .v ),所以裡面也是由一個 module 組成,不同的地方在於,一般 verilog 檔案會燒進電路板裡,而 testbench 只是讓我們在電腦模擬的檔案罷了。因此,可以用一些方便的工具執行,例如, for 迴圈、integer…等。
這裡介紹一種最簡單的模板供大家使用,有興趣的,可以再自己深入研究。
首先,我們必須觀察我們寫好的 verilog 檔,判斷哪些是 input ,哪些是 output。
舉例來說:
module Binary_to_Grey (din, dout); input [4-1:0] din; output [4-1:0] dout; assign dout = {1'b0, din[4-1:1]} ^ din; endmodule由此範例可得, input 為 din,而 output 為 dout。
剛剛說過,tb 也是一個 verilog 檔案,所以起手式都一樣。
`timescale 1ns / 1ps //時間參數,請打在 tb 的第一行 module Testbench(); // 由於 tb 是沒有 input , output 的,所以括號裡面不用放東西。 // 直接進入 參數設定 reg [3:0] din; wire[3:0] dout; reg CLK; . . . endmodule
接著要呼叫我們剛剛寫好的程式碼(呼叫裡面的 module)
`timescale 1ns / 1ps module Testbench(); // 由於 tb 是沒有 input , output 的,所以括號裡面不用放東西。 // 直接進入 參數設定 reg [3:0] din; wire[3:0] dout; reg CLK; Binary_to_Grey name(.din(din), .dout(dout) ); . . . endmodule
- name 是幫這個呼叫取一個名字,因為在之後寫 code 時,每個 module 通常不會只呼叫一次,所以每一個呼叫都需要一個名字,且不能一樣。
設置常數。
`timescale 1ns / 1ps module Testbench(); reg [3:0] din; wire[3:0] dout; reg CLK; Binary_to_Grey name(.din(din), .dout(dout) ); always #5 CLK = ~CLK; initial CLK = 1'b1; . . . endmodulealways #5 指的是每經過 5 個 cycles ,就跑一次後面的式子。
initial 指的是程式一開始執行時,就先運行這一行式子。
開始運算
`timescale 1ns / 1ps module Testbench(); reg [3:0] din; wire[3:0] dout; reg CLK; Binary_to_Grey name(.din(din), .dout(dout) ); always #5 CLK = ~CLK; initial CLK = 1'b1; initial begin din = 4'b0000; //給 din 一個初值 repeat (2 ** 4) begin //重複 2 ^ 4 次以下這個動作 @(posedge CLK) test; //先照抄 @(nededge CLK) din = din + 1'b1; // din 變成下一個數字 end end $finish . . . endmodule
寫 test 部分
`timescale 1ns / 1ps module Testbench(); reg [3:0] din; wire[3:0] dout; reg CLK; Binary_to_Grey name(.din(din), .dout(dout) ); always #5 CLK = ~CLK; initial CLK = 1'b1; initial begin din = 4'b0000; //給 din 一個初值 repeat (2 ** 4) begin //重複 2 ^ 4 以下這個動作 @(posedge CLK) test; //先照抄 @(nededge CLK) din = din + 1'b1; // din 變成下一個數字 end end $finish task test; begin if ( (din ===4'd0 && dout === 4'b0000) || (din === 4'd1 && dout === 4'b0001) || (din === 4'd2 && dout === 4'b0011) || (din === 4'd3 && dout === 4'b0010) || (din === 4'd4 && dout === 4'b0110) || (din === 4'd5 && dout === 4'b0111) || (din === 4'd6 && dout === 4'b0101) || (din === 4'd7 && dout === 4'b0100) || (din === 4'd8 && dout === 4'b1100) || (din === 4'd9 && dout === 4'b1101) || (din === 4'd10&& dout === 4'b1111) || (din === 4'd11&& dout === 4'b1110) || (din === 4'd12&& dout === 4'b1010) || (din === 4'd13&& dout === 4'b1011) || (din === 4'd14&& dout === 4'b1001) || (din === 4'd15&& dout === 4'b1000) ) $display("din = %d, dout = %d, got it !!\n", din, dout); else $display("din = %d, dout = %d, OOOOOOoooooops!!!\n", din, dout); end endtask endmoduletask 是一個用來寫重複指令的方法,在這裡我們將所有正確的結果放在 if 裡面,這樣我們就能判斷我們的程式碼是否正確。
ps. $display 類似 c 語言的 printf 。
always #(cyc/2) clk = ~clk;
initial begin
$fsdbDumpfile("as4_2.fsdb");
$fsdbDumpvars;
end