简单Verilog编写数字电路的各个模块,必须伴随着一testbench文件用作仿真验证。简单的module当然可以使用简单的Verilog编写一个testbench进行简单的仿真,但一旦遇到功能较为复杂时,Verilog语言的灵活性不足C/C++这类语言。SystemVerilog作为一门针对验证的面向对象的编程语言,能够更好的描述时序,同时具有面向对象语言的灵活性以及重复使用性,能够为工程仿真带来许多便利。接下来,简单使用SystemVerilog对序列检测的module进行仿真验证。
一、工程框架
本工程测试一个使用状态机检测“1110010”序列的module,包含五个文件,具体描述如下:
├── top.sv:顶层模块,用以连接下列各个模块
│
│ ├── module_bfm.sv: 模块的bus functional model,描述用以连接的interface
│
│ ├── tester.sv: 产生仿真的时序信号
│
│ ├── coverage.sv: 对模块覆盖率的检测
│
│ ├── module.v: 待仿真验证的模块
由于module是使用Verilog编写的,不能直接使用Interface, 而需要按照Verilog的规范进行连接,而其余SV的module直接使用BFM连接。
module top();
seq_fsm_bfm bfm();
coverage converage_i(bfm);
tester tester_i(bfm);
seq_fsm seq_fsm_i(
.in(bfm.in),
.out(bfm.out),
.state(bfm.state),
.clk(bfm.clk),
.reset(bfm.rst_n)
);
endmodule
二、SystemVerilog的interface使用
使用Verilog是对于module端口的连接,不管使用顺序还是使用名称连接都容易出错以及极为繁琐。SystemVerilog的interface有点类似C/C++的类,将模块的整个端口信号封装,让其在module之间共享,十分方便,该出定义一个用于连接序列检测module的端口。同时interface还能提供initial与task,以方便时钟以及描述一些简单信号的产生,这里描述时钟、复位信号以及数据信号的交互。
//Bus Functional Model
interface seq_fsm_bfm();
logic clk;
logic in;
logic out;
logic[0:2] state;
logic rst_n;
initial begin
clk=0;
forever #10 clk = ~clk;
end
task reset();
rst_n = 0;
@(posedge clk);
@(posedge clk);
rst_n = 1;
endtask
task send_data(bit in_data);
@(posedge clk) in = in_data;
endtask
endinterface //seq_fsm_interface
三、功能覆盖率
功能覆盖率实际为定义一些coverpoint用以采样信号,进而测试所采的信号是否包含所有状态。我们仅进行一个简单的功能覆盖率的例子,步骤可以简单分为:1、定义coverage;2、采样
module coverage(seq_fsm_bfm bfm);
logic in;
logic out;
logic[2:0] state;
covergroup cg_cov;
coverpoint in;
coverpoint out;
coverpoint state;
endgroup
cg_cov oc;
initial begin
oc = new();
forever begin
@(posedge bfm.clk);
in = bfm.in;
out = bfm.out;
state = bfm.state;
oc.sample();
end
end
endmodule
coverpoint在默认情况下是包含所有的状态,即以上3比特的state包含8个状态,“000”,“001”,”010“......,对于一些比较多的状态,可以使用排除或列举。使用以上得到的覆盖率报告如下:
# COVERGROUP COVERAGE:
# ----------------------------------------------------------------------------------------------------
# Covergroup Metric Goal/ Status
# At Least
# ----------------------------------------------------------------------------------------------------
# TYPE /top/converage_i/cg_cov 100.0% 100 Covered
# Coverpoint cg_cov::in 100.0% 100 Covered
# Coverpoint cg_cov::out 100.0% 100 Covered
# Coverpoint cg_cov::state 100.0% 100 Covered
# Covergroup instance \/top/converage_i/oc 100.0% 100 Covered
# Coverpoint in 100.0% 100 Covered
# covered/total bins: 2 2
# missing/total bins: 0 2
# bin auto['b0] 498 1 Covered
# bin auto['b1] 502 1 Covered
# Coverpoint out 100.0% 100 Covered
# covered/total bins: 2 2
# missing/total bins: 0 2
# bin auto['b0] 994 1 Covered
# bin auto['b1] 4 1 Covered
# Coverpoint state 100.0% 100 Covered
# covered/total bins: 8 8
# missing/total bins: 0 8
# bin auto['b000] 394 1 Covered
# bin auto['b001] 222 1 Covered
# bin auto['b010] 127 1 Covered
# bin auto['b011] 142 1 Covered
# bin auto['b100] 64 1 Covered
# bin auto['b101] 31 1 Covered
# bin auto['b110] 14 1 Covered
# bin auto['b111] 4 1 Covered
四、测试器Tester
这里测试器比较简单,只是复位一下BFM以及产生随机的01信号。
module tester(seq_fsm_bfm bfm);
initial begin
bit in_data;
bfm.reset();
repeat(1000) begin
// $display("@%0t\n",$time);
in_data = ($random) %2;
bfm.send_data(in_data);
end
$stop;
end
endmodule
五、Modelsim自动化仿真
Modelsim仿真步骤可以大概分为编译与仿真,仿真每次编译、添加波形、仿真十分繁琐。可以编写do脚本进行自动化仿真,do文件如下:
if [file exists "work"] {vdel -all}
vlib work
vlog top.sv tester.sv seq_fsm_bfm.sv coverage.sv seq_fsm.v #编译文件,注意路径,这里与do文件同目录
vopt top -o top_optimized +acc +cover=sbfec+seq_fsm(rtl). #指定顶层文件,优化
vsim top_optimized -coverage #开始仿真
set NoQuitOnFinish 1
onbreak {resume}
log /* -r
add wave seq_fsm_i/* #添加波形
run -all
vcover report seq_fsm.ucdb -cvg -details #报告覆盖率
编写好run.do文件,Linux下直接运行
vsim -do run.do
参考:
克里斯·斯皮尔, SystemVerilog验证 测试平台编写指南
Ray Salemi, The UVM Primer