UVM驱动与采样模块时序异常及#1延时必要性解析
UVM总线driver时序异常问题分析
问题背景
基于UVM编写了总线driver类,同时编写了sampler_n_dummy_gnt模拟从端响应逻辑,精简后代码如下:
driver代码
class driver extends uvm_driver #(bus_trans); ... task run_phase(); forever begin seq_item_port.get_next_item(bus_trans_h); drive_item(bus_trans_h); ... end ... endtask task drive_item(); vif.sel = 1; vif.req = 0; @(posedge vif.clk); vif.req = 1; // #1; // this delay needed to prolong req for 1 clock if (vif.gnt == 1) @(posedge vif.clk); else begin while (vif.gnt == 0) @(posedge vif.clk); end end vif.req = 0; endtask endclass
从端sampler代码
module sampler_n_dummy_gnt; logic clk, rstn; int rdelay; bus_interface vif(clk, rstn); always #10ns clk = ~clk; always @(posedge vif.req) begin rdelay = $urandom_range(0,5); if (rdelay != 0) begin for (int i=0; i<rdelay; i=i+1) begin vif.gnt = 0; @(posedge vif.clk); end end vif.gnt = 1; end endmodule
逻辑规则说明
- driver侧协议要求:新事务发起时拉高sel信号,等待1个时钟周期后拉高req信号;之后等待gnt信号为高,再过1个时钟周期后拉低req信号。
- 从端sampler逻辑:检测到req拉高后,随机生成0~5范围内的延迟值rdelay,若rdelay不为0则等待对应数量的时钟周期后拉高gnt,若rdelay为0则直接拉高gnt。
异常现象
第一笔事务传输正常,第二笔事务出现时序异常:时钟7上升沿处gnt拉低,预期req应保持高电平直到时钟8上升沿检测到gnt拉高后再拉低,但实际从时钟7开始req始终保持为0,未按预期拉高。初步怀疑时钟7上升沿时逻辑检测到gnt=1,跳过了if-else分支的时钟等待逻辑,直接执行了req=0赋值;添加代码中注释掉的#1延时语句后,即可得到符合预期的时序。
预期与实际时序对比如下:
(预期时序) (实际时序) 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ clk _| |_| |_| |_| |_| |_| |_| |_| |_ _| |_| |_| |_| |_| |_| |_| |_| |_ _______________ ___________ _______________ ___________ sel _| |___| _| |___| ___________ _______ ___________ req _____| |_______| _____| |_______________ ___________ ___ ___________ ___ gnt _____________| |___| _____________| |___|
根因解释
该异常是SystemVerilog仿真调度的delta cycle竞争导致的,核心逻辑如下:
- 信号残留:第一笔事务传输完成后,
vif.gnt会保持高电平,直到第二笔事务的req拉高触发sampler进程,才会被sampler拉低。 - 同沿执行顺序无保证:第二笔事务中,driver在时钟上升沿执行
vif.req = 1后,会立刻触发sampler的always @(posedge vif.req)进程。此时driver的gnt电平判断逻辑、sampler的gnt拉低逻辑在同一个仿真时间点执行,仿真器不保证二者的执行顺序:- 若sampler先执行将gnt拉为0,driver读到gnt=0,进入等待循环,时序符合预期;
- 若driver先执行if判断,sampler还未及时拉低gnt,driver读到的是上一笔事务残留的gnt=1,就会直接进入if分支,仅等待1个时钟沿就拉低req,出现req提前变低的异常。
第一笔事务传输正常只是仿真器刚好调度顺序正确,不代表代码不存在问题。
#1延时的作用原理
添加#1延时后,driver在拉高req后会等待1个时间单位,让出仿真执行权,等sampler进程完成gnt信号的更新,再读取gnt的电平值。此时读到的是sampler设置的正确电平,不会再误判gnt为高,时序就符合预期。
工程提示:直接加
#1是临时解决方式,更规范的做法是在总线接口中定义clocking block,明确约定信号驱动、采样相对于时钟沿的偏移,从语法层面彻底避免同沿调度竞争问题。
内容的提问来源于stack exchange,提问作者renvill
相关产品推荐
相关产品推荐

