with one click
testbench-generator
基于RTL-Spec分析结果,生成包含激励序列和参考模型的完整SystemVerilog Testbench
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
基于RTL-Spec分析结果,生成包含激励序列和参考模型的完整SystemVerilog Testbench
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
| name | testbench-generator |
| description | 基于RTL-Spec分析结果,生成包含激励序列和参考模型的完整SystemVerilog Testbench |
你是一个Testbench代码生成专家,专注于:
{
"inputs": {
"analysis_result": {
"type": "JSON",
"source": "rtl-spec-analyzer Skill",
"description": "包含module_info、features、feature_mappings"
}
}
}
输入JSON结构:
{
"module_info": {
"name": "模块名",
"clock_signals": ["..."],
"reset_signals": ["..."],
"ports": [...],
"circuit_type": "combinational|sequential|pipeline"
},
"features": [...],
"feature_mappings": [
{
"feature_id": "...",
"name": "...",
"pin_assignments": [...],
"stimulus_strategy": "...",
"expected_outputs": [...],
"latency_cycles": 1
}
]
}
基于module_info生成基础结构:
`timescale 1ns/1ps
module tb_<module_name>;
// 参数定义
parameter CLK_PERIOD = 10;
// DUT信号声明(根据module_info.ports生成)
// - input 映射为 logic
// - output 映射为 logic(或使用wire连接DUT输出)
// - inout 映射为 logic(视情况)
// DUT实例化
// 时钟/复位生成
// 测试激励区域
// Reference Model(与DUT时序同步)
// 自动比对(基于有效信号窗口)
// 测试报告
endmodule
针对每个feature_mapping,生成独立的initial块:
激励生成原则:
stimulus_strategy生成精确的0/1序列@(posedge clk)控制时序,禁止使用#delay或repeat(n)激励代码模板:
// Test Case: <feature_id> - <name>
// Description: <description>
// Latency: <latency_cycles> cycles
initial begin
// 等待复位完成
@(posedge rst_n);
@(posedge clk);
// 标记测试开始(用于生成check_window)
test_start[<index>] = 1'b1;
// 设置输入激励(根据pin_assignments)
<pin_name> = <value>;
...
// 等待一个时钟让DUT采样
@(posedge clk);
// 继续下一组测试
<pin_name> = <next_value>;
@(posedge clk);
// 标记测试完成
test_done[<index>] = 1'b1;
end
核心原则:Reference Model 必须与 DUT 具有完全相同的时序特性。
建模策略(根据circuit_type选择):
// Reference Model 使用组合逻辑(与DUT一致)
always_comb begin
ref_<output> = <combinational_logic_expression>;
end
// 或:assign ref_<output> = <expression>;
// Reference Model 使用时序逻辑(与DUT一致)
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n)
ref_<output> <= <reset_value>;
else if (<enable_condition>)
ref_<output> <= <next_logic>;
end
// 关键:使用与DUT相同的时钟沿和使能条件
// Reference Model 复制DUT的流水线深度
// DUT有N级流水线,Reference也有N级
logic [<width-1>:0] ref_pipe[<N>]; // N级流水线寄存器
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int i = 0; i < N; i++)
ref_pipe[i] <= <reset_value>;
end else if (<pipeline_enable>) begin
ref_pipe[0] <= <stage0_logic>; // 第1级
ref_pipe[1] <= ref_pipe[0]; // 第2级
...
ref_pipe[N-1] <= ref_pipe[N-2]; // 第N级
end
end
assign ref_<output> = ref_pipe[N-1];
// Reference Model - 必须精确复制DUT的状态转移逻辑
// 方式1: 显式状态编码(如果DUT状态编码已知)
typedef enum logic [N-1:0] {
STATE_IDLE = <value>,
STATE_BUSY = <value>,
STATE_DONE = <value>
} state_t;
state_t ref_state, ref_next_state;
// 状态寄存器(与DUT使用相同的时钟沿和复位)
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n)
ref_state <= STATE_IDLE; // 与DUT相同的复位状态
else
ref_state <= ref_next_state;
end
// 次态组合逻辑(必须与DUT逻辑完全一致)
always_comb begin
ref_next_state = ref_state; // 默认保持
case (ref_state)
STATE_IDLE: begin
if (<start_condition>)
ref_next_state = STATE_BUSY;
end
STATE_BUSY: begin
if (<done_condition>)
ref_next_state = STATE_DONE;
end
STATE_DONE: begin
ref_next_state = STATE_IDLE;
end
default: ref_next_state = STATE_IDLE;
endcase
end
// 输出逻辑(Mealy/Moore模型与DUT一致)
always_comb begin
ref_<moore_output> = (ref_state == STATE_DONE); // Moore输出
ref_<mealy_output> = <condition> ? 1'b1 : 1'b0; // Mealy输出
end
FSM检查策略:
// 方案1: 检查最终输出
always_ff @(posedge clk) begin
if (rst_n) begin
if (dut_output !== ref_output)
$display("[ERROR] State=%0s: Output mismatch!", ref_state.name());
end
end
// 方案2: 检查状态转移(如果DUT有状态输出)
always_ff @(posedge clk) begin
if (rst_n && dut_state_valid) begin
if (dut_state !== ref_state)
$display("[ERROR] State mismatch! DUT=%h, REF=%h", dut_state, ref_state);
end
end
关键要点:
#delay 来模拟延迟核心方案:不使用固定延迟,而是基于DUT的输出有效信号进行检查。
生成自动比较代码:
// 自动比对逻辑
logic [31:0] error_count;
logic [31:0] pass_count;
// 检查窗口生成(根据DUT特性自动确定)
logic check_valid; // 当为1时进行检查
// 方案A: 组合逻辑 - 使用 negedge clk 检查(输入稳定后)
// 适用于:输出在时钟低电平期间稳定的场景
generate
if (CIRCUIT_TYPE == "combinational") begin : comb_check
always @(negedge clk) begin
if (rst_n && check_valid) begin
if (dut_<output> !== ref_<output>) begin
$display("[ERROR] Time=%0t: <feature_id> - Mismatch!", $time);
$display(" DUT: %h, REF: %h", dut_<output>, ref_<output>);
error_count <= error_count + 1;
end else begin
pass_count <= pass_count + 1;
end
end
end
end
// 方案B: 时序逻辑 - 使用 posedge clk 检查(DUT和Ref都已更新)
// 适用于:时序逻辑输出在上升沿更新
else begin : seq_check
always_ff @(posedge clk) begin
if (rst_n && check_valid) begin
if (dut_<output> !== ref_<output>) begin
$display("[ERROR] Time=%0t: <feature_id> - Mismatch!", $time);
$display(" DUT: %h, REF: %h", dut_<output>, ref_<output>);
error_count <= error_count + 1'b1;
end else begin
pass_count <= pass_count + 1'b1;
end
end
end
end
endgenerate
检查窗口生成方法:
根据 DUT 的输出特性自动生成 check_valid:
// 方法1: DUT 有明确的 valid 信号
assign check_valid = dut_valid_out;
// 方法2: DUT 没有 valid 信号 - 基于输入使能延迟生成
// 对于单周期时序逻辑
always_ff @(posedge clk) begin
check_valid <= input_valid; // 延迟1周期
end
// 方法3: 多周期操作(流水线)- 使用移位寄存器追踪
logic [<max_latency>-1:0] check_delay;
always_ff @(posedge clk) begin
if (rst_n && start_op)
check_delay <= {{<max_latency>-1{1'b0}}, 1'b1};
else
check_delay <= {check_delay[<max_latency>-2:0], 1'b0};
end
assign check_valid = check_delay[<latency>-1];
检查时序原则:
{
"check_timing_principles": [
{
"circuit_type": "组合逻辑",
"check_scheme": "negedge clk",
"description": "输入在上升沿改变,在下降沿稳定后检查"
},
{
"circuit_type": "单周期时序",
"check_scheme": "posedge clk + 延迟1周期的valid",
"description": "等待DUT响应后检查"
},
{
"circuit_type": "流水线",
"check_scheme": "posedge clk + 移位寄存器valid",
"description": "根据级数延迟检查"
}
]
}
关键规则:
// 时钟
initial begin
clk = 1'b0;
forever #(CLK_PERIOD/2) clk = ~clk;
end
// 复位
initial begin
rst_n = 1'b0;
#(CLK_PERIOD * 5);
rst_n = 1'b1;
end
// 仿真结束控制
logic [N-1:0] test_done;
initial begin
wait(&test_done);
#(CLK_PERIOD * 10);
$display("Simulation Completed");
$display("Total Pass: %0d", pass_count);
$display("Total Fail: %0d", error_count);
if (error_count == 0)
$display("TEST PASSED");
else
$display("TEST FAILED");
$finish;
end
文件名:tb_<module_name>.sv
完整代码结构(v1.0):
`timescale 1ns/1ps
module tb_<module_name>;
parameter CLK_PERIOD = 10;
// 信号声明
logic clk;
logic rst_n;
// DUT输入信号
logic [<width-1>:0] <input_port_name>;
...
// DUT输出信号
logic [<width-1>:0] <output_port_name>;
...
// DUT实例化
<module_name> u_dut (
.<port_name> (<signal_name>),
...
);
// 时钟/复位生成
...
// Reference Model(与DUT时序同步)
// 根据DUT的circuit_type生成对应的Reference Model
// 组合逻辑: 使用 always_comb
// 时序逻辑: 使用 always_ff (同clk, 同enable)
// 流水线: 使用相同级数的pipe寄存器
...
// 检查窗口生成(基于有效信号,无固定延迟)
logic check_valid;
// 根据DUT特性生成check_valid...
// 自动比对逻辑(基于check_valid)
// 组合逻辑: 在 negedge clk 检查
// 时序逻辑: 在 posedge clk 检查
...
// 测试激励
// 每个功能点一个initial块
// 使用 @(posedge clk) 控制时序
// 禁止使用 #delay 或 repeat(n)
...
// 仿真控制
...
endmodule
详细示例代码请参见 references/examples.md,包含以下示例:
严格禁止:
// 禁止使用
#1; // 固定时间延迟
repeat(3) @(posedge clk); // 魔术数字重复
#(CLK_PERIOD * 2); // 基于周期的延迟
// 必须使用
@(posedge clk); // 时钟沿同步
@(negedge clk); // 时钟沿同步
input_valid to output_valid // 基于有效信号
{
"reference_model_timing_sync": [
{
"dut_type": "组合逻辑",
"reference_model": "always_comb",
"check_timing": "negedge clk"
},
{
"dut_type": "单周期时序",
"reference_model": "always_ff @(posedge clk)",
"check_timing": "posedge clk(延迟1周期)"
},
{
"dut_type": "N级流水线",
"reference_model": "N级 always_ff pipe",
"check_timing": "posedge clk(延迟N周期)"
}
]
}
优先使用 DUT 自带的 valid/done 信号:
// 最佳:DUT有valid信号
assign check_valid = dut.valid_out;
// 次优:基于输入追踪延迟
always_ff @(posedge clk)
check_valid <= input_valid; // 或移位寄存器
// 避免:固定延迟计数器
always @(posedge clk)
if (cnt == 3) check_valid = 1;
### 4. **并行激励**
- 不同功能点的测试可以用独立的`initial`块并行执行
- 每个`initial`块应有自己的`done`信号
- 注意处理信号冲突(建议串行执行或分配不同时间片)
### 5. **时序精确性**
- 输入信号应在时钟沿前稳定(setup time)
- 组合逻辑输出在`negedge clk`检查(确保输入已稳定)
- 时序逻辑输出在`posedge clk`检查(确保DUT和Ref都已更新)
## 通用处理原则(未覆盖类型)
当遇到未明确定义的 `circuit_type`(如多周期数据通路、异步电路、总线接口等),遵循以下通用原则构建 Reference Model:
### 1. **电路特性分析**
首先识别电路的核心时序特性:
```json
{
"circuit_characteristics_analysis": [
{
"dimension": "状态保持",
"question": "电路是否有内部状态?",
"strategy": "有时态: 使用状态寄存器;无时态: 使用组合逻辑"
},
{
"dimension": "延迟特性",
"question": "输出相对输入是否有延迟?",
"strategy": "固定延迟: 使用移位寄存器;可变延迟: 使用握手信号追踪"
},
{
"dimension": "时钟域",
"question": "是否存在多个时钟域?",
"strategy": "单时钟: 简单同步;多时钟: 需要同步器或握手"
},
{
"dimension": "控制方式",
"question": "是电平控制还是脉冲触发?",
"strategy": "电平敏感: 使用组合/时序;边沿触发: 使用时序 + 脉冲检测"
}
]
}
根据输出特性确定检查时机:
{
"check_timing_rules": [
{
"condition": "输出由输入直接组合产生",
"check_scheme": "@(negedge clk)",
"reason": "输入在posedge改变,在negedge稳定后检查"
},
{
"condition": "输出由时序逻辑产生",
"check_scheme": "@(posedge clk) + 1周期延迟",
"reason": "DUT和Ref都在posedge更新,需要等稳定"
},
{
"condition": "DUT有 valid/done 信号",
"check_scheme": "@(posedge dut.valid_out)",
"reason": "使用DUT自带有效信号,最可靠"
},
{
"condition": "可变延迟操作",
"check_scheme": "基于输入启动信号移位追踪",
"reason": "每个操作的延迟可能不同"
}
]
}
Step 1: 端口分析
Step 2: Spec语义提取
Step 3: 构建最简Reference Model
// 通用模板:根据分析结果填充
module ref_model (
input clk,
input rst_n,
// DUT输入
input [<width-1>:0] <inputs>,
// DUT输出
output [<width-1>:0] ref_<outputs>
);
// 1. 状态寄存器(如果需要)
// logic [<width-1>:0] ref_state;
// 2. 延迟追踪(如果需要)
// logic [<delay>-1:0] valid_pipe;
// 3. 参考逻辑(根据Spec实现)
// always_comb / always_ff 根据特性选择
endmodule
Step 4: 验证策略
{
"special_circuit_types": [
{
"circuit_type": "多周期数据通路",
"reference_model_strategy": "用计数器追踪当前周期数",
"check_strategy": "计数达到时检查"
},
{
"circuit_type": "异步FIFO",
"reference_model_strategy": "使用标准FIFO模型(不同时钟)",
"check_strategy": "读使能有效时检查"
},
{
"circuit_type": "总线接口 (AXI/AHB)",
"reference_model_strategy": "实现协议状态机",
"check_strategy": "根据协议握手信号检查"
},
{
"circuit_type": "Latch-based",
"reference_model_strategy": "使用电平敏感 always_latch",
"check_strategy": "使能信号无效后检查"
},
{
"circuit_type": "Gated Clock",
"reference_model_strategy": "Reference使用原始时钟",
"check_strategy": "DUT和Ref在相同有效沿检查"
}
]
}
{
"workflow": {
"upstream_skill": {
"name": "rtl-spec-analyzer",
"output": {
"name": "analysis_result",
"format": "JSON",
"data_structure": {
"module_info": "RTL端口信息",
"circuit_type": "电路类型(v1.0新增)",
"features": "功能点列表",
"feature_mappings": "功能点到Pin的映射",
"latency_cycles": "功能延迟周期(v1.0新增)"
}
}
},
"current_skill": {
"name": "testbench-generator",
"role": "本Skill",
"input": "analysis_result",
"tasks": [
"生成Testbench框架",
"生成各功能点的激励代码",
"生成与DUT时序同步的Reference Model",
"生成基于有效信号的自动比对逻辑"
],
"output": {
"file_name": "tb_<module>.sv",
"description": "完整Testbench文件"
}
}
}
}
分析RTL Design和Specification,提取功能点并完成Signal到Pin的映射分析
将拆分后的Verilog子模块合并回完整RTL,并用testbench-generator+iverilog完成最终验证
在 Verilog partition 之后、merge 之前对 part 文件做 Yosys 优化,并用 Verible 进行语法/lint 兜底
利用LLM Agent自动对Verilog RTL设计进行拆分,按always块生成多个子模块