如何修改UVM的bit bash序列适配DUT的2周期读写延迟?
嘿,这个问题我之前帮好几个同事踩过坑——核心就是UVM寄存器模型默认假设读写1个周期就能完成,但你的DUT需要2个周期才会实际执行并返回结果,所以uvm_reg_bit_bash_seq这类内置序列写完立刻读,读回的还是旧值,自然就比对失败了。
先看你遇到的典型时序不匹配错误:
Writing a 1 in bit #0 of register "ral_pgm.DIFF_STAT_CORE1" with initial value 'h0000000000000000 yielded 'h0000000000000000 instead of 'h0000000000000001
下面是几种靠谱的解决方式,按全局适配的优先级排序:
1. 给总线适配器/驱动加延迟(推荐全局使用)
寄存器适配器(uvm_reg_adapter)是RegModel和总线之间的翻译器,你可以在总线的driver或者sequence里,添加2个时钟周期的等待,确保DUT完成操作后再继续流程。
比如在总线driver里处理:
class my_bus_driver extends uvm_driver #(my_bus_seq_item); virtual task run_phase(uvm_phase phase); forever begin seq_item_port.get_next_item(req); // 先执行总线读写的驱动逻辑 drive_bus_transfer(req); // 关键:等待2个时钟周期,给DUT足够时间完成操作 repeat(2) @(posedge vif.clk); seq_item_port.item_done(); end endtask endclass
如果你的总线sequence是单独定义的,也可以在sequence的body里加延迟:
class my_bus_rw_seq extends uvm_sequence #(my_bus_seq_item); virtual task body(); my_bus_seq_item req; req = my_bus_seq_item::type_id::create("req"); start_item(req); finish_item(req); // 等待DUT完成读写的2个周期 repeat(2) @(posedge p_sequencer.vif.clk); endtask endclass
这种方式的好处是全局生效,所有寄存器的读写都会自动带上延迟,不用逐个修改寄存器。
2. 重写寄存器的do_write/do_read方法(适合特殊寄存器)
如果只有少数几个寄存器需要2周期延迟,直接在寄存器类里重写读写方法就行:
class DIFF_STAT_CORE1_reg extends uvm_reg; function new(string name = "DIFF_STAT_CORE1_reg"); super.new(name, 64, UVM_NO_COVERAGE); endfunction virtual task do_write(uvm_reg_item rw); // 先调用默认的写操作逻辑 super.do_write(rw); // 等待2个时钟周期 repeat(2) @(posedge get_parent_block().vif.clk); endtask virtual task do_read(uvm_reg_item rw); super.do_read(rw); repeat(2) @(posedge get_parent_block().vif.clk); endtask endclass
这种方式更灵活,适合单个寄存器有特殊时序的场景。
3. 用UVM内置的访问延迟参数(需要适配器支持)
UVM提供了set_access_delay方法,可以给寄存器或整个block设置访问延迟:
// 在RegModel的build_phase里配置 ral_pgm.DIFF_STAT_CORE1.set_access_delay(2, UVM_WRITE); ral_pgm.DIFF_STAT_CORE1.set_access_delay(2, UVM_READ); // 或者给整个PGM block的所有寄存器都设置 ral_pgm.set_access_delay(2, UVM_WRITE | UVM_READ);
不过要注意,这个参数不会自动生效,你需要在总线适配器或driver里读取这个延迟值,然后等待对应的周期:
// 在adapter的reg2bus方法里获取延迟 int delay = rw.reg.get_access_delay(rw.kind); // 然后在driver里等待delay个周期 repeat(delay) @(posedge vif.clk);
最后验证
调整完之后,再跑uvm_reg_bit_bash_seq,序列就会等待足够的时间再读回寄存器值,自然就能通过比对了。
内容的提问来源于stack exchange,提问作者Emman

