目录
4.assign语句:用于连接DUT端口与Testbench中的信号
测试设计
DUT (Design Under Test)例化指的是将你要测试的设计模块实例化到测试平台(Testbench)中。设计模块到测试平台,一般有3个文件:
- design 有输入输出。
- testbench, 验证平台,没有输入输出。
- testcase 应用案例。产生激励,check 响应
三个模块的分工:testbench是最终的硬件部分,它将design 与tescaset 连接起来,使testcase生成的输入流向你想要测试的对象(design).
😀 如果把test放在 testbench 里,就是两个部分。vscode 配置的Verilog 仿真环境,基本就是两个文件:一个 design.v, 一个 design_testbench.v
模块testAdd生成了module halfAdd的输入,并显示更改。module halfAdd是design 的一部分。
$monitor, 监视器,变量一旦有变化,就会有打印。
Testbench
Verilog Testbench的编写主要涉及模块实例化、信号声明、激励生成和响应监控等步骤。
1. 端口声明
- 连接到DUT(待测设计)的输入端口需声明为
reg
类型 - DUT的输出端口需声明为
wire
类型。
2.基本模块例化
模块例化是将一个模块定义实例化为设计中的一个具体实例。基本语法如下:
module_name instance_name (
.port_name(signal_name)
);
module_name:要例化的模块名称。
instance_name:实例的名称,需唯一。
port_name:模块的端口名称。
signal_name:连接到端口的信号名称。
端口连接方式 推荐使用带名字的连接方式 .port_name(signal),可读性高,不易出错
举例:
假设你有一个设计模块名为 my_module,它有如下端口定义:
module my_module (
input clk,
input rst_n,
input [7:0] data_in,
output [7:0] data_out
);
// 模块功能代码
endmodule
module tb_my_module;
// 定义信号
reg clk, rst_n;
reg [7:0] data_in;
wire [7:0] data_out;
// 实例化 DUT
my_module uut (
.clk (clk),
.rst_n (rst_n),
.data_in (data_in),
.data_out(data_out)
);
// 其他测试逻辑,如时钟生成、复位控制、激励等
endmodule
3.激励生成与响应监控
初始块(initial):用于生成测试激励。
always块:用于生成时钟信号或复位信号等周期性激励
4.assign语句:用于连接DUT端口与Testbench中的信号
5.保存波形
例子:
// this is a latches example:0703 lily
module latches( //design
output Qsimple, Qclear, QpreClear,
input D, ena1 ,ena2, clr, pre
);
wire clr_n,pre_n,ena1_n;
reg QsimpleReg, QclearReg, QpreClearReg;
assign clr_n=clr;
assign pre_n=pre;
assign ena1_n=ena1;
assign #1 Qsimple=QsimpleReg; //assign 并发执行
assign #2 Qclear=QclearReg;
assign #2 QpreClear=QpreClearReg;
always @(D,ena1) begin //latch1
if (ena1 ==1'b1)
QsimpleReg <= D; //功能定义里操作的都是内部信号
end
always @(D,ena1_n) begin //latch2
if(clr == 1'b1)
QclearReg <= 1'b0; // 非阻塞赋值,用在有时钟控制的逻辑
else if (ena1_n == 1'b0)
QclearReg <= D;
end
always @(D, pre_n, ena2, clr_n) begin //latch3
`ifdef DC
`elsr
if (pre_n==1'b0 && clr_n =1'b0)
$strobe(
"\n *** Time=%04d, pre_n=%01b and clr_n=%01b asserted.\n"
);
`endif
if (clr_n==1'b0) QpreClearReg <= 1'b0;
else if (pre_n == 1'b0) QpreClearReg <= 1'b1;
else if (ena2 == 1'b1) QpreClearReg <= 0;
end
endmodule//latches
//*****************以下是testbench ******************//
module latchesTst; // Testbench
wire QsimpleW,Qclearw,Qpreclearw,D,ena1,ena2,clr,pre; //端口声明
reg DReg,ena1Reg,ena2Reg,clrReg,preReg;
assign D=DReg; //assign 语句
assign ena1 = ena1Reg;
assign ena2 = ena2Reg;
assign clr= clrReg;
assign pre =preReg;
always@(DReg) //激励
#15 DReg <= ~DReg;
always@(ena2Reg)
#10 ena2Reg <= ~ena2Reg;
initial begin
#0 DReg =1'b0;//The only time EVER to use '#0
ena1Reg =1'b0;// without deep doubts!
preReg= 1'b0;
clrReg= 1'b0;
#2.5 ena2Reg = 1'b0;
#2.5 ena1Reg = 1'b1;
#10 clrReg= 1'b1;
#10 clrReg= 1'b0;
#10 clrReg= 1'b1;
#10 clrReg= 1'b0;
#04 ena1Reg = 1'b0;
#03 preReg= 1'b1;
#10 preReg= 1'b0;
#10 preReg= 1'b0;
#10 clrReg= 1'b1;
#10 preReg= 1'b0;
#10 clrReg= 1'b0;
#10 preReg= 1'b1;
#10 clrReg = 1'b1;
#04 ena1Reg = 1'b1;
#01 preReg = 1'b1;
#10 preReg= 1'b0;
#10 preReg= 1'b0;
#10 clrReg= 1'b1;
#10 preReg= 1'b0;
#10 clrReg= 1'b0;
#10 preReg= 1'b1;
#10 clrReg= 1'b1;
#20 $finish;
end
latches latchesInst1( //模块例化
.Qsimple(Qsimplew),
.Qclear(Qclearw),
.QpreClear(QpreClearw),
.D(D),
.ena1(ena1),
.ena2(ena2),
.clr(clr),
.pre(pre)
);
initial //保存波形
begin
$dumpfile("latches_tb.vcd"); //生成的波形 vcd文件名称
$dumpvars(0, latchesTst); //参与仿真的tb模块名称
end
endmodule //latchesTst
运行后,选取latch1的波形:
always @(D,ena1) begin
if (ena1 ==1'b1)
QsimpleReg <= D; //功能定义里操作的都是内部信号
end
task 和 function
task, 必须加begin ... end .......... 作用:写test case
task是一种用于定义可重用功能模块的方法。它可以看作是一个过程或子程序,可以在模块内部或外部调用。任务允许我们将一个复杂的操作分解成较小的模块,以提高代码的可读性和可维护性。
任务支持复杂的时序控制功能,这使得task成为了处理涉及时间延迟、事件触发以及同步操作的理想选择。#delays, @, wait
调用方式: 任务是通过调用来执行的,并且只有在调用时才执行。如果定义了任务但没有调用它,那么这个任务是不会执行的
function. 特点:0 延迟。必须返回一个值,返回的变量= function name, 这里返回relocate。用作:design
- 调用方式:
- 用途:
- Task: 适用于需要处理时序逻辑的场景,例如延迟、事件控制等 。
- Function: 主要用于纯组合逻辑,替代简单的表达式操作。
system task
系统任务的使用方式与使用“task...endtask”构造定义的常规任务相同。它们之间的区别在于首字符,该字符始终为“$
$readmemh,读一个文件,初始化 memory
system function
和function 相同,区别是有 $ 头:$ time 返回仿真时间, $random
XMR
Verilog 具备一种机制,用于全局引用网络、寄存器、事件、任务和函数,该机制称为跨模块引用,或XMR。 比如 top.dut.xxx
跨模块引用,或有时称为层次化引用,可以有多种形式:
- 模块内不同范围的引用
- 模块之间的引用
- 向下引用
- 向上引用
Hierarchical Module
每个模块定义中都有一个静态作用域,可以用来定位任何标识符。例如,在以下示例中,
不同区域内 x 不一样
用A.D.x 访问
Verilog 2001 new features
input 类型 位宽 名字;
参数定义
列表,用 ,号 分割
vector
- 定义:在Verilog中,向量(vector)是一组信号的集合,通常用于表示多位宽的数据。向量可以是wire或reg类型,声明时需要指定其位宽。例如,wire[3:0] vec;声明了一个4位宽的线网向量vec 。
- 用途:向量常用于总线和多位数据处理,使得设计者能够方便地对一组信号进行操作。通过向量,可以简化代码并提高可读性。
array
二维数组表示 array_2D[127:0][127:0]。 访问某个元素 array_2D[8][8]
net and real 也可以放到数组里
传递参数,使用 . 进行访问
#(.size(1), .width(12), .size(4096));
generate