Verilog 代码设计完成后,还需要进行重要的步骤,即逻辑功能仿真。仿真激励文件称之为 testbench,放在各设计模块的顶层,以便对模块进行系统性的例化调用进行仿真。 毫不夸张的说,对于稍微复杂的 Verilog 设计,如果不进行仿真,即便是经验丰富的老手,99.9999% 以上的设计都不会正常的工作。不能说仿真比设计更加的重要,但是一般来说,仿真花费的时间会比设计花费的时间要多。有时候,考虑到各种应用场景,testbench 的编写也会比 Verilog 设计更加的复杂。所以,数字电路行业会具体划分设计工程师和验证工程师。 下面,对 testbench 做一个简单的学习。 testbench 结构划分testbench 一般结构如下:其实 testbench 最基本的结构包括信号声明、激励和模块例化。 根据设计的复杂度,需要引入时钟和复位部分。当然更为复杂的设计,激励部分也会更加复杂。根据自己的验证需求,选择是否需要自校验和停止仿真部分。 当然,复位和时钟产生部分,也可以看做激励,所以它们都可以在一个语句块中实现。也可以拿自校验的结果,作为结束仿真的条件。 实际仿真时,可以根据自己的个人习惯来编写 testbench,这里只是做一份个人的总结。 testbench 仿真举例前面的章节中,已经写过很多的 testbench。其实它们的结构也都大致相同。 下面,我们举一个数据拼接的简单例子,对 testbench 再做一个具体的分析。 一个 2bit 数据拼接成 8bit 数据的功能模块描述如下: - 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 ; //数据计数
- data_r <= {data_r[5:0], din} ; //数据拼接
- 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
- //计数为 3 且第 4 个数据输入时,同步输出数据输出使能信号
- 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变量,而是用相关寄存器对其进行assign赋值
- assign dout_en = dout_en_r;
- endmodule
对应的 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
仿真结果如下。由图可知,数据整合功能的设计符合要求: [url=https://www.runoob.com/wp-content/uploads/2020/09/Yp7UIofA10Bynfgh.png][/url]
|