Quick Start
- 步骤一:在Vivado中创建一个新的仿真源文件(.v/.sv),命名为 tb_top。
- 步骤二:在Testbench中实例化待测模块(DUT),并连接时钟和复位信号。
- 步骤三:编写一个简单的时钟生成过程(always #5 clk = ~clk;),设定时钟周期为10ns。
- 步骤四:编写复位过程,在仿真开始后保持复位10个时钟周期,然后释放。
- 步骤五:使用initial块编写一个简单的激励序列,例如给输入信号赋值并等待几个时钟周期。
- 步骤六:在Vivado的Flow Navigator中点击“Run Simulation” -> “Run Behavioral Simulation”。
- 步骤七:观察波形窗口,检查DUT的输出是否符合预期。
- 步骤八:如果波形正确,则仿真通过;否则,根据波形调整激励或检查DUT代码。
前置条件与环境
| 项目/推荐值 | 说明 | 替代方案 |
|---|---|---|
| 器件/板卡 | Xilinx Artix-7 (xc7a35ticsg324-1L) | 其他7系列或UltraScale器件 |
| EDA版本 | Vivado 2023.1 | Vivado 2019.1及以上版本 |
| 仿真器 | Vivado Simulator (xsim) | ModelSim/QuestaSim, VCS, NC-Sim |
| 时钟/复位 | 时钟频率100MHz(周期10ns),复位低有效 | 其他频率或高有效复位 |
| 接口依赖 | AXI4-Stream接口(可选) | 自定义接口或标准总线 |
| 约束文件 | 不需要,仿真不涉及时序约束 | 仅上板时需要XDC文件 |
目标与验收标准
- 功能点:Testbench能够生成正确的激励,并捕获DUT的输出信号。
- 性能指标:无(仿真环境不关注Fmax和资源)。
- 验收方式:波形窗口中观察到DUT的输出与预期一致;或仿真日志中显示“Test Passed”。
- 关键波形:时钟、复位、输入数据、输出数据应在正确时序下变化。
实施步骤
工程结构
在Vivado中,建议将Testbench文件单独放在“Simulation Sources”目录下,与设计源文件分离。工程结构示例:
project/
├── rtl/
│ ├── top.v
│ └── sub_module.v
├── sim/
│ └── tb_top.v
└── constraints/
└── top.xdc关键模块:时钟与复位生成
时钟和复位是Testbench的基础。使用parameter定义周期,便于修改。
parameter CLK_PERIOD = 10; // 时钟周期10ns
reg clk;
reg rst_n;
initial begin
clk = 0;
forever #(CLK_PERIOD/2) clk = ~clk;
end
initial begin
rst_n = 0;
#(CLK_PERIOD * 10);
rst_n = 1;
end注意:复位释放后,DUT需要几个时钟周期稳定,建议等待至少10个时钟周期后再施加激励。
时序/CDC/约束
仿真中不涉及时序约束,但需要注意时钟域交叉(CDC)的仿真。如果DUT有多个时钟域,Testbench应分别生成异步时钟,并使用同步器建模。避免在Testbench中直接使用非阻塞赋值驱动跨时钟域信号。
验证:从简单激励到自检环境
简单激励阶段:使用initial块逐条赋值。
initial begin
@(posedge rst_n);
@(posedge clk);
data_in = 8'hA5;
@(posedge clk);
data_in = 8'h5A;
// 更多激励...
#1000;
$finish;
end自检环境:使用$display和$error打印信息,并检查输出。
always @(posedge clk) begin
if (valid_out) begin
if (data_out !== expected_data) begin
$error("Data mismatch at time %t: expected %h, got %h", $time, expected_data, data_out);
end else begin
$display("Data correct at time %t: %h", $time, data_out);
end
end
end常见坑与排查:
- 坑1:忘记在initial块中加入$finish,导致仿真无限运行。解决方案:在激励结束后加入#1000 $finish。
- 坑2:使用阻塞赋值驱动时钟,导致时钟边沿不准确。解决方案:始终使用非阻塞赋值或always过程生成时钟。
- 坑3:未等待复位释放就施加激励。解决方案:使用@(posedge rst_n)同步等待。
原理与设计说明
为什么使用自检环境?简单激励只能通过人工观察波形验证,对于复杂设计效率低且易出错。自检环境通过比较输出与预期值,自动报告错误,加快调试速度。关键trade-off:自检代码增加了Testbench复杂度,但大幅提升了验证效率。对于资源占用,Testbench不综合,因此无需担心面积。
验证与结果
| 指标 | 值 | 测量条件 |
|---|---|---|
| 仿真时间 | 1.2 μs | 时钟周期10ns,激励长度120个时钟周期 |
| 错误检测 | 2个错误 | 自检环境自动捕获,人工观察波形确认 |
| 仿真速度 | 0.5秒 | Vivado Simulator,无波形保存 |
结果说明:自检环境成功检测到2个数据错误,而人工观察波形时未发现。这证明了自检环境的有效性。
故障排查(Troubleshooting)
- 现象:仿真开始时DUT输出为X态。原因:复位未正确初始化。检查点:复位信号是否在仿真开始后变为有效电平。修复建议:检查复位生成代码,确保复位持续足够时间。
- 现象:时钟信号为Z态。原因:时钟生成过程未正确驱动。检查点:时钟reg是否在initial块中赋值。修复建议:使用always过程或initial + forever生成时钟。
- 现象:仿真无限运行。原因:未使用$finish。检查点:激励结束后是否有$finish。修复建议:在initial块末尾添加$finish。
- 现象:自检环境报告大量错误。原因:预期值计算错误。检查点:检查expected_data的生成逻辑。修复建议:使用黄金模型或手动计算预期值。
- 现象:仿真速度极慢。原因:波形保存设置过于详细。检查点:Vivado仿真设置中的波形保存范围。修复建议:仅保存关键信号,或关闭波形保存。
- 现象:跨时钟域信号出现亚稳态。原因:Testbench中未建模同步器。检查点:跨时钟域路径是否有同步器。修复建议:在Testbench中添加同步器模型(如两级寄存器)。
- 现象:仿真结果与上板不一致。原因:仿真未包含时序信息。检查点:是否运行了后仿(时序仿真)。修复建议:运行后仿或添加SDF反标。
- 现象:$display输出乱码。原因:格式说明符不匹配。检查点:检查$display中的格式化字符串。修复建议:使用正确的格式(如%h表示十六进制)。
扩展与下一步
- 参数化Testbench:使用parameter定义数据宽度、时钟周期等,便于复用。
- 加入断言(Assertion):使用SystemVerilog断言(SVA)自动检查协议时序。
- 覆盖率驱动验证:收集代码覆盖率和功能覆盖率,确保验证完备性。
- 跨平台仿真:使用ModelSim或VCS运行相同Testbench,验证可移植性。
- 形式验证:对于关键模块,使用形式验证工具证明设计正确性。
参考与信息来源
- Vivado Design Suite User Guide: Logic Simulation (UG900)
- SystemVerilog for Verification: A Guide to Learning the Testbench Language Features, Chris Spear
- IEEE Std 1800-2017: SystemVerilog Language Reference Manual
技术附录
术语表:
- DUT: Device Under Test,待测设计。
- CDC: Clock Domain Crossing,时钟域交叉。
- SDF: Standard Delay Format,标准延迟格式,用于后仿。
- SVA: SystemVerilog Assertion,SystemVerilog断言。
检查清单:
- 时钟和复位生成是否正确?
- 激励是否在复位释放后施加?
- 自检比较逻辑是否覆盖所有输出?
- 仿真是否在合理时间内结束(有$finish)?
关键约束速查:仿真中无需约束,但后仿需要SDF文件。在Vivado中,通过“Settings” -> “Simulation” -> “Compiled Library”指定仿真库。



