Search code examples
verilogsystem-verilog

SV/Verilog timing of sampling and driving signals


I have a class driver (coded in UVM) and a module sampler_n_dummy_gnt as follows (simplified for the purpose of this question):

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
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

Basically, the driver emulates a protocol that specifies that it should assert sel whenever there's a new transaction. One cycle after, it should assert req. It then waits until gnt is high and only deasserts req one cycle after.

The sampler waits for the assertion of req and based on the randomly generated number waits for a certain number of clock cycles before it asserts gnt. So either there's no delay or a few delays with maximum 5 cycles.

The first transaction is OK until I run into the 2nd transaction. At posedge of clock 7, the gnt deasserted and so req should remain high until it sees the gnt assert at clock 8. However, I never saw req assert anymore from clock 7 onwards (i.e. it stayed 0). I suspected that at posedge of clock 7, it sees gnt = 1 but skipped the if-else condition and goes straight to drive req = 0. So I have to add the line I commented above in order to get the expected scenario. Could someone help explain why the delay is needed?

                (Expected)                               (Actual)
        1   2   3   4   5   6   7   8          1   2   3   4   5   6   7   8
       _   _   _   _   _   _   _   _          _   _   _   _   _   _   _   _  
clk  _| |_| |_| |_| |_| |_| |_| |_| |_      _| |_| |_| |_| |_| |_| |_| |_| |_
       _______________     ___________        _______________     ___________
sel  _|               |___|                 _|               |___|
           ___________         _______            ___________         
req  _____|           |_______|             _____|           |_______________   
                   ___________     ___                    ___________     ___
gnt  _____________|           |___|         _____________|           |___|


Solution

  • I'm assuming clk, vif.clk, vif.pclk are really all the same signal.

    Your code has a number of problems with dealing with synchronous signals.

    • You should only use one clock edge event, and never an edge of another signal unless you are dealing with asynchronous logic.
    • When making assignments to synchronous signals, always use nonblocking assignments <=
    • Make sure the first call to a task is synchronized to a clock edge

    Since you did not post the complete code, I can only make this a suggestion

    class driver extends uvm_driver #(bus_trans);
        ...
        task run_phase();
           @(posedge vif.clk; // initial sync
           forever begin
                while (!seq_item_port.try_next_item(bus_trans_h)
                   @(posedge vif.clk;
                drive_item(bus_trans_h);
                ...
            end
        ...
        endtask
    
        task drive_item();
            vif.sel <= 1;
            vif.req <= 0;
            @(posedge vif.clk);
            vif.req <= 1;
            @(posedge vif.clk iff vif.gnt);
            vif.req <= 0;
        endtask
    endclass