SystemVerilog验证编写(1)
我错了。不立flag,不定期更新~
前面几次推送已经给出了FIFO的RTL综合设计和SV写法的ref模型
本次的Testbench就是基于这两次的代码,进行验证。
Testbench的常见组成模块如下,由复位、产生、发送、接收、计分板比对几个模块组成。
本次验证代码就是通过给上述两个FIFO发送相同的信号与指令内容,通过把两个FIFO的输出发送到check task中进行比对,确定RTL写法与时序是否正确,看其是否可以实现正确的功能。
首先要连接DUT模块和TB模块,那么使用interface接口进行连接。
一般来说,我们在接口中使用时钟块去同步TB中的信号,确定端口方向。
`timescale 1ns / 100ps //****************************************************************** // Author:SJTU_chen // Date: 2019/10/26 // Version: v1.0 // Module Name: fifo-interface // Project Name: SystemVerilog Lab1 //******************************************************************* interface fifo_io(input bit clock); logic reset_n,valid_in,valid_out,valid_out1,ready_in,ready_in1; logic [63:0] data_in; logic [1:0] wstrb; logic [31:0] data_out,data_out1; clocking cb @(posedge clock); default input #1ns output #1ns; output reset_n; output valid_in; output data_in; output wstrb; input valid_out,valid_out1; input data_out,data_out1; input ready_in,ready_in1; endclocking: cb modport TB(clocking cb, output reset_n); endinterface:fifo_io在顶层模块中把其连接起来,部分代码如下:
`timescale 1ns / 100ps //****************************************************************** // Author:SJTU_chen // Date: 2019/10/26 // Version: v1.0 // Module Name: fifo-testbench_top // Project Name: SystemVerilog Lab1 //******************************************************************* module fifo_test_top(); parameter simulation_cycle = 10; bit SystemClock = 0; fifo_io top_io(SystemClock); test t(top_io); fifo_dut fifo_dut_inst( .clock(top_io.clock), .reset_n(top_io.reset_n), .valid_in(top_io.valid_in), .wstrb(top_io.wstrb), .data_in(top_io.data_in), .valid_out(top_io.valid_out), .ready_in(top_io.ready_in), .data_out(top_io.data_out) ); fifo_ref fifo_ref_inst( .clock(top_io.clock), .reset_n(top_io.reset_n), .valid_in(top_io.valid_in), .wstrb(top_io.wstrb), .data_in(top_io.data_in), .valid_out(top_io.valid_out1), .ready_in(top_io.ready_in1), .data_out(top_io.data_out1) ); always begin #(simulation_cycle/2) SystemClock = ~SystemClock; end endmodule
接下来就是编写TB模块了,如上图所示:
首先我们编写的是复位reset task模块:
//task reset task reset(); fi_io.reset_n = 1'b0; fi_io.cb.wstrb <= '0; fi_io.cb.valid_in <= '1; fi_io.cb.data_in <= '0; repeat(2) @fi_io.cb; fi_io.cb.reset_n <= 1'b1; repeat(15) @fi_io.cb; endtask: reset
上述代码请仔细看,有点儿意思,很多同学的写法不是这样的,这个在Soc设计方法学课程中会讲到~
接下来就是gen task
//task gen task gen(); send_data.delete(); wstrb_quene.delete(); repeat(120) begin wstrb_quene.push_back({$urandom}); send_data.push_back({$urandom,$urandom}); end endtask: gen发送 send task:
task send(); send_payload(); endtask:send task send_payload(); // static int count = 0; static int pkts_checked = 0; while(send_data.size() != 0) begin if (pkts_checked < 80) begin fi_io.cb.data_in <= send_data.pop_front(); fi_io.cb.wstrb <= wstrb_quene.pop_front(); fi_io.cb.valid_in <= $urandom; // count++; end else begin fi_io.cb.data_in <= send_data.pop_front(); fi_io.cb.wstrb <= 2'b11; fi_io.cb.valid_in <= 1'b1; // count++; end end endtask:send_payload接收 task
//task recv_dut task recv_dut(); get_payload_fromdut(); endtask: recv_dut //task recv_ref task recv_ref(); get_payload_fromref(); endtask: recv_ref比对模块部分代码:
function void check(); string message; static int pkts_checked = 0; if (!compare(message)) begin $display("\n%m\n[ERROR]%t Packet #%0d %s\n", $realtime, pkts_checked++, message); $finish; end $display("[NOTE]%t Packet #%0d %s", $realtime, pkts_checked++, message); endfunction: check
通过以上比对,我们就可以看出dut设计是否正确,请注意,前提条件是保证我们设计的ref模型是没问题的。
最终的仿真截图如下:
下次,我们讲讲怎么使用面向对象的结构去验证~