二进制转格雷码模块仿真验证失败:输出不匹配问题排查
问题:SystemVerilog测试平台中DUT输出与Scoreboard预期格雷码不匹配
基于SystemVerilog搭建了包含driver、monitor和scoreboard的测试平台环境,在Synopsys VCS仿真过程中,被测器件(DUT)输出的gray信号频繁与scoreboard计算的预期格雷码不匹配。
相关代码实现
Design Module
`timescale 1ns / 1ps module binary_to_gray(input [3:0] binary, output [3:0] gray); assign gray[3] = binary[3]; // MSB remains the same assign gray[2] = binary[3] ^ binary[2]; // XOR for bit 2 assign gray[1] = binary[2] ^ binary[1]; // XOR for bit 1 assign gray[0] = binary[1] ^ binary[0]; // XOR for bit 0 endmodule
Top_Testbench Module
`timescale 1ns / 1ps `include "interface.sv" `include "test" module top_tb; // Top-level testbench module intf i_intf(); // Interface instantiation test t1(i_intf); // Test instantiation // Instantiate the DUT (Device Under Test) binary_to_gray DUT( .binary(i_intf.binary), .gray(i_intf.gray) ); endmodule
Environment Module
`timescale 1ns / 1ps `include "transaction.sv" `include "generator.sv" `include "driver.sv" `include "monitor" `include "scoreboard" class environment; generator gen; driver driv; monitor mon; scoreboard scb; mailbox m1, m2; virtual intf vif; function new(virtual intf vif); this.vif = vif; m1 = new(); m2 = new(); gen = new(m1); driv = new(vif, m1); mon = new(vif, m2); scb = new(m2); endfunction task test(); fork gen.main(); driv.main(); mon.main(); scb.main(); join endtask endclass
Driver Module
`timescale 1ns / 1ps class driver; virtual intf vif; mailbox gen2driv; function new(virtual intf vif, mailbox gen2driv); this.vif = vif; this.gen2driv = gen2driv; endfunction task main(); transaction trans; repeat(5) begin gen2driv.get(trans); // Receive transaction vif.binary = trans.binary; // Drive input to DUT #10; // Increased propagation delay trans.gray = vif.gray; // Capture output trans.display("Driver"); end endtask endclass
Generator Module
`timescale 1ns / 1ps class generator; transaction trans; mailbox gen2driv; function new(mailbox gen2driv); this.gen2driv = gen2driv; endfunction task main(); repeat(5) begin trans = new(); if (!trans.randomize()) $error("Failed to randomize transaction!"); trans.display("Generator"); gen2driv.put(trans); // Send transaction to driver end endtask endclass
Interface Module
`timescale 1ns / 1ps interface intf(); logic [3:0] binary; logic [3:0] gray; endinterface
Transaction Module
`timescale 1ns / 1ps class transaction; rand bit [3:0] binary; // Randomizable binary input bit [3:0] gray; // Output to store Gray code function void display(string name); $display("--------------------------"); $display("%s", name); $display("--------------------------"); $display("Binary = %b", binary); $display("Gray = %b", gray); $display("--------------------------"); endfunction endclass
Test Module
`timescale 1ns / 1ps `include "environment.sv" program test(intf i_intf); environment env; initial begin env = new(i_intf); env.test(); $finish; end endprogram
Monitor Module
`timescale 1ns / 1ps class monitor; virtual intf vif; // Virtual interface for DUT connections mailbox mon2scb; // Mailbox to send transactions to the scoreboard // Constructor function new(virtual intf vif, mailbox mon2scb); this.vif = vif; this.mon2scb = mon2scb; endfunction // Main task to observe DUT behavior task main(); transaction trans; repeat(5) begin #10; // Increased propagation delay (align with Driver) trans = new(); // Initialize the transaction object trans.binary = vif.binary; // Capture binary input trans.gray = vif.gray; // Capture Gray code output trans.display("Monitor"); // Display the transaction details mon2scb.put(trans); // Send the transaction to the scoreboard end endtask endclass
Scoreboard Module
`timescale 1ns / 1ps class scoreboard; mailbox mon2scb; logic [3:0] expected_gray; // Declare at the class level function new(mailbox mon2scb); this.mon2scb = mon2scb; endfunction task main(); transaction trans; repeat(5) begin mon2scb.get(trans); // Receive transaction expected_gray[3] = trans.binary[3]; expected_gray[2] = trans.binary[3] ^ trans.binary[2]; expected_gray[1] = trans.binary[2] ^ trans.binary[1]; expected_gray[0] = trans.binary[1] ^ trans.binary[0]; if (trans.gray == expected_gray) $display("PASS: Binary = %b, Gray = %b", trans.binary, trans.gray); else $error("FAIL: Binary = %b, Expected Gray = %b, DUT Gray = %b", trans.binary, expected_gray, trans.gray); end endtask endclass
错误示例
FAIL: Binary = 1111, Expected Gray = 1000, DUT Gray = 1100 FAIL: Binary = 0001, Expected Gray = 0001, DUT Gray = 1100
补充信息:仿真工具为Synopsys VCS,所有模块通过虚拟接口实例化连接,错误在不同二进制输入下表现不一致。
问题根源分析
- Driver与Monitor的时序不同步:Driver先驱动输入,等待10ns后采样;而Monitor启动后直接等待10ns就采样,两者采样动作无同步机制。由于fork并行执行,Monitor的#10可能在Driver更新输入前触发,导致采到上一周期的DUT输出或未稳定信号。
- 竞态条件导致采样错误:Environment中四个任务(gen、driv、mon、scb)并行启动,Monitor采样与Driver驱动无依赖关系,无法保证采样的是当前输入对应的输出。
- DUT实现无问题:DUT的二进制转格雷码逻辑符合标准算法,Scoreboard预期值计算也正确,问题不在DUT本身。
修复方案
1. 用事件同步Driver与Monitor的采样动作
在接口中添加同步事件,Driver完成输入驱动并等待稳定后触发事件,Monitor等待该事件后再采样,确保采样时机正确。
修改Interface模块
`timescale 1ns / 1ps interface intf(); logic [3:0] binary; logic [3:0] gray; event drv_done; // 添加同步事件 endinterface
修改Driver的main任务
task main(); transaction trans; repeat(5) begin gen2driv.get(trans); // 接收事务 vif.binary = trans.binary; // 驱动DUT输入 #10; // 等待DUT输出稳定 ->vif.drv_done; // 触发事件通知Monitor可以采样 trans.gray = vif.gray; // 捕获输出 trans.display("Driver"); end endtask
修改Monitor的main任务
task main(); transaction trans; repeat(5) begin @(vif.drv_done); // 等待Driver触发的同步事件 trans = new(); // 初始化事务对象 trans.binary = vif.binary; // 捕获当前输入 trans.gray = vif.gray; // 捕获对应稳定输出 trans.display("Monitor"); // 显示事务信息 mon2scb.put(trans); // 发送到Scoreboard end endtask
2. 可选:调整任务启动顺序(辅助优化)
在Environment的test任务中,先启动Generator确保第一个事务进入mailbox后,再启动Driver和Monitor,避免Driver初始阻塞:
task test(); fork begin gen.main(); // 先启动Generator产生事务 end begin driv.main(); mon.main(); scb.main(); end join endtask
修复效果说明
修改后,Monitor会严格在Driver完成输入驱动并等待DUT输出稳定后采样,确保每次采样的都是当前输入对应的正确格雷码输出,Scoreboard预期值与DUT输出将完全匹配,解决不匹配问题。
内容的提问来源于stack exchange,提问作者Kotupalli Venkata Siva Teja
相关产品推荐
相关产品推荐

