Verilog 代碼設(shè)計(jì)完成后,還需要進(jìn)行重要的步驟,即邏輯功能仿真。仿真激勵文件稱之為 testbench,放在各設(shè)計(jì)模塊的頂層,以便對模塊進(jìn)行系統(tǒng)性的例化調(diào)用進(jìn)行仿真。
毫不夸張的說,對于稍微復(fù)雜的 Verilog 設(shè)計(jì),如果不進(jìn)行仿真,即便是經(jīng)驗(yàn)豐富的老手,99.9999% 以上的設(shè)計(jì)都不會正常的工作。不能說仿真比設(shè)計(jì)更加的重要,但是一般來說,仿真花費(fèi)的時(shí)間會比設(shè)計(jì)花費(fèi)的時(shí)間要多。有時(shí)候,考慮到各種應(yīng)用場景,testbench 的編寫也會比 Verilog 設(shè)計(jì)更加的復(fù)雜。所以,數(shù)字電路行業(yè)會具體劃分設(shè)計(jì)工程師和驗(yàn)證工程師。
下面,對 testbench 做一個(gè)簡單的學(xué)習(xí)。
testbench 結(jié)構(gòu)劃分
testbench 一般結(jié)構(gòu)如下。
其實(shí) testbench 最基本的結(jié)構(gòu)包括信號聲明、激勵和模塊例化。
根據(jù)設(shè)計(jì)的復(fù)雜度,需要引入時(shí)鐘和復(fù)位部分。當(dāng)然更為復(fù)雜的設(shè)計(jì),激勵部分也會更加復(fù)雜。根據(jù)自己的驗(yàn)證需求,選擇是否需要自校驗(yàn)和停止仿真部分。
當(dāng)然,復(fù)位和時(shí)鐘產(chǎn)生部分,也可以看做激勵,所以它們都可以在一個(gè)語句塊中實(shí)現(xiàn)。也可以拿自校驗(yàn)的結(jié)果,作為結(jié)束仿真的條件。
實(shí)際仿真時(shí),可以根據(jù)自己的個(gè)人習(xí)慣來編寫 testbench,這里只是做一份個(gè)人的總結(jié)。
testbench 仿真舉例
前面的章節(jié)中,已經(jīng)寫過很多的 testbench。其實(shí)它們的結(jié)構(gòu)也都大致相同。下面,列舉一個(gè)數(shù)據(jù)拼接的簡單例子,對 testbench 再做一個(gè)具體的分析。
◆ 一個(gè) 2bit 數(shù)據(jù)拼接成 8bit 數(shù)據(jù)的功能模塊描述如下。
module data_consolidation
(
input clk ,
input rstn ,
input [1:0] din , //data in
input din_en ,
output [7:0] dout ,
output dout_en //data out
);
// data shift and counter
reg [7:0] data_r ;
reg [1:0] state_cnt ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
state_cnt <= 'b0 ;
data_r <= 'b0 ;
end
else if (din_en) begin
state_cnt <= state_cnt + 1'b1 ; //數(shù)據(jù)計(jì)數(shù)
data_r <= {data_r[5:0], din} ; //數(shù)據(jù)拼接
end
else begin
state_cnt <= 'b0 ;
end
end
assign dout = data_r ;
// data output en
reg dout_en_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
dout_en_r <= 'b0 ;
end
//計(jì)數(shù)為 3 且第 4 個(gè)數(shù)據(jù)輸入時(shí),同步輸出數(shù)據(jù)輸出使能信號
else if (state_cnt == 2'd3 & din_en) begin
dout_en_r <= 1'b1 ;
end
else begin
dout_en_r <= 1'b0 ;
end
end
//這里不直接聲明dout_en為reg變量,而是用相關(guān)寄存器對其進(jìn)行assign賦值
assign dout_en = dout_en_r;
endmodule
◆ 對應(yīng)的 testbench 描述如下,增加了文件讀寫的語句。
`timescale 1ns/1ps
//============== (1) ==================
//signals declaration
module test ;
reg clk;
reg rstn ;
reg [1:0] din ;
reg din_en ;
wire [7:0] dout ;
wire dout_en ;
//============== (2) ==================
//clock generating
real CYCLE_200MHz = 5 ; //
always begin
clk = 0 ; #(CYCLE_200MHz/2) ;
clk = 1 ; #(CYCLE_200MHz/2) ;
end
//============== (3) ==================
//reset generating
initial begin
rstn = 1'b0 ;
#8 rstn = 1'b1 ;
end
//============== (4) ==================
//motivation
int fd_rd ;
reg [7:0] data_in_temp ; //for self check
reg [15:0] read_temp ; //8bit ascii data, 8bit \\n
initial begin
din_en = 1'b0 ; //(4.1)
din = 'b0 ;
open_file("../tb/data_in.dat", "r", fd_rd); //(4.2)
wait (rstn) ; //(4.3)
# CYCLE_200MHz ;
//read data from file
while (! $feof(fd_rd) ) begin //(4.4)
@(negedge clk) ;
$fread(read_temp, fd_rd);
din = read_temp[9:8] ;
data_in_temp = {data_in_temp[5:0], din} ;
din_en = 1'b1 ;
end
//stop data
@(posedge clk) ; //(4.5)
#2 din_en = 1'b0 ;
end
//open task
task open_file;
input string file_dir_name ;
input string rw ;
output int fd ;
fd = $fopen(file_dir_name, rw);
if (! fd) begin
$display("--- iii --- Failed to open file: %s", file_dir_name);
end
else begin
$display("--- iii --- %s has been opened successfully.", file_dir_name);
end
endtask
//============== (5) ==================
//module instantiation
data_consolidation u_data_process
(
.clk (clk),
.rstn (rstn),
.din (din),
.din_en (din_en),
.dout (dout),
.dout_en (dout_en)
);
//============== (6) ==================
//auto check
reg [7:0] err_cnt ;
int fd_wr ;
initial begin
err_cnt = 'b0 ;
open_file("../tb/data_out.dat", "w", fd_wr);
forever begin
@(negedge clk) ;
if (dout_en) begin
$fdisplay(fd_wr, "%h", dout);
end
end
end
always @(posedge clk) begin
#1 ;
if (dout_en) begin
if (data_in_temp != dout) begin
err_cnt = err_cnt + 1'b1 ;
end
end
end
//============== (7) ==================
//simulation finish
always begin
#100;
if ($time >= 10000) begin
if (!err_cnt) begin
$display("-------------------------------------");
$display("Data process is OK!!!");
$display("-------------------------------------");
end
else begin
$display("-------------------------------------");
$display("Error occurs in data process!!!");
$display("-------------------------------------");
end
#1 ;
$finish ;
end
end
endmodule // test