文章简介
个人学习总结2 - Verilog最基本模块与Testbench仿真
个人向!!!
参考:数字逻辑基础与Verilog设计(英文原书第3版)McGrawHill Education
参考:机械工业出版社中文翻译版 - 国外电子与电气工程技术丛书
软件:Quartus_II,Vivado,ChatGPT
此文章包括Verilog基本模块代码,Testbench代码,Tb语句讲解,仿真结果与分析!
个人NTU在读,欢迎指正
基本的语法见上一篇文章 :)
状态机电路、Tb及仿真详见下一篇文章,本篇篇幅太长了 :)
目录
1. 组合逻辑基础模块
1.1. 4to1MUX多路选择器
- 4个输入,使用2bit的选择信号S来决定谁作为输出。
- 第一种写法,使用if / else,这种写法综合出来比较复杂
module test1
(
input [2:0] w0, //这里使用3bit的待选输入信号,后面TB看着清晰点
input [2:0] w1,
input [2:0] w2,
input [2:0] w3,
input [1:0] S, //选择信号S
output reg [2:0] Y //输出Y
);
always@(*)
begin
if(S == 0) //S等于几就选w几
Y = w0;
else if(S == 1)
Y = w1;
else if(S == 2)
Y = w2;
else
Y = w3;
end
endmodule
- 第二种写法,使用case,这种方法综合出来直接是多bit输入的MUX,比较简单
module test1
(
input [2:0] w0, w1, w2, w3,
input [1:0] S,
output reg [2:0] Y
);
always@(*)
begin
case(S)
0: Y = w0;
1: Y = w1;
2: Y = w2;
3: Y = w3;
endcase
end
endmodule
- Testbench如下
`timescale 1ns / 1ps //时间单位 / 精度
module tb_test1;
reg [2:0] w0_test, w1_test, w2_test, w3_test; //将输入声明为reg
reg [1:0] S;
wire [2:0] Y; //输出声明为wire
// Instantiate the design under test (DUT) 实例化连接
test1 dut (
.w0(w0_test), // Connect w0_test to w0 input of DUT
.w1(w1_test),
.w2(w2_test),
.w3(w3_test),
.S(S),
.Y(Y)
);
// Test测试
initial
begin
// Apply initial values to the reg signals
w0_test = 4; // Now w0_test is assigned a value
w1_test = 5;
w2_test = 6;
w3_test = 7;
S = 2'b00; // S = 0, expect Y = w0_test
// Display header
$display("S\tw0\tw1\tw2\tw3\tY");
$monitor("%b\t%b\t%b\t%b\t%b\t%b", S, w0_test, w1_test, w2_test, w3_test, Y);
// Apply different values for S
#10 S = 2'b01; // S = 1, expect Y = w1_test
#10 S = 2'b10; // S = 2, expect Y = w2_test
#10 S = 2'b11; // S = 3, expect Y = w3_test
#30 $finish; //30ns后结束仿真
end
endmodule
超详细的Testbench解释
1. timescale
- timescale 指令用于指定仿真的时间单位和精度,它是可选的,但通常在testbench和模块文件的顶部添加,以确保仿真时间的一致性和精确度。
- 1ns表示仿真时间的单位为纳秒;1ps表示时间的精度为皮秒。
- 如果改成1ns / 1ns,
2. 模块声明
- 还是用“module”和“endmodule”来声明测试模块,名称通常会加上“_tb”来表示是tb模块。
- 记得直接在名称后面加上“;”分号。
3. 输入输出声明
- Tb中不需要显式地声明输入和输出,测试嘛,实例化连接到原模块模拟一下就行。
- reg:用于表示可以在仿真过程中赋值的信号,通常用于输入信号,表示它们在仿真中可以存储和改变。“_test”这里表示tb中的测试信号,和原模块做区分。
- wire:用于表示通过组合逻辑传输的信号,通常用于输出信号,它的值由模块或其他逻辑驱动。
4. 实例化连接
- 正常的模块调用,把原模块调用到tb里,在tb中模块实例化是必须的,其的目的是将设计单元DUT嵌入到tb中,使其可以进行仿真验证。
5. 测试部分
- 由于是组合逻辑验证,每种情况跑一次就行,所以用了initial。
- 给所有的输入进行赋值,作为一开始的情况,这里直接给S赋值为0,所以一开始就会选择w0_test
- "$display"是Verilog中用于打印消息到仿真输出(控制台)的系统任务,它的作用类似于“printf”函数,在仿真过程中输出文本或变量的值。
- display不写其实不影响tb的仿真,它的意义主要体现在输出格式化和调试便捷性上,它用来打印头部或者一些标识性的消息,在tb中用来在仿真开始时打印一行标题,这样你就可以清楚地知道每一列代表的信号是什么。(左图没有display,右图有display)(Tcl控制台截图)
- "S\tw0\tw1\tw2\tw3\tY"是字符串,表示输出的文本格式,"\t"是制表符"Tab",用于在输出中加入空格,使得每个变量名之间有一定的间隔,使输出更加整齐,查看输出结果时,变量名将以表格形式排布。这部分就和python差不多,“ ”内输入什么就打印出什么。
- "$monitor"是Verilog 中用于实时监控信号的系统任务,用来实时监控并输出信号的值,它的行为会随着信号的变化而实时更新,display只执行一次,而monitor会持续监视指定的信号,一旦这些信号的值发生变化,它就会重新输出最新的信号值。
- monitor会一直执行,直到最大仿真时间,或者"$finish"被调用表示结束。(机考题出现&