Assignments using clocking block not working inside fork

Below is some code from my interface and driver class definitions. When the code runs as-is, I never see the valid data cycle appear on the interface. I know that forks started inside of a for loop don’t actually start executing until the loop is done. But it seems that the for loop is advancing simulation time in such a way that when the threads start, I’ve already missed the posedge of my clocking block. If I throw “@this.vif.drv_cb” before the first interface assignment, I then see the valid cycle appear on my interface - but one cycle later than I want it. This adds a cycle before every valid transaction, making it impossible for me to drive back-toback valid transactions.

I have similar driver code for other agents that do not use forks. I’m able to drive back-to-back transactions on those interfaces just fine. So it seems almost certain the for-loop/fork combination is what’s causing my pain here.

Thanks in advance for any suggestions.

interface my_intf (input logic clk, input logic rst_n);

  localparam WIDTH = 16;
  localparam NUM_LANE = 8;

  logic [NUM_LANE-1:0] vld;
  logic [NUM_LANE-1:0] dat[WIDTH-1:0];

  clocking drv_cb @(posedge clk);
    output vld;
    output data;
  endclocking : drv_cb

  clocking mon_cb @(posedge clk);
    input rst_n;
    input vld;
    input data;
  endclocking : mon_cb

endinterface : my_intf


class my_driver#(parameter NUM_LANE=1) extends uvm_driver #(my_item#(NUM_LANE));

...

  virtual task run_phase(uvm_phase phase);
    forever begin
      this.seq_item_port.get_next_item(this.req);
      this.drive_txn(this.req);
      this.seq_item_port.item_done();
    end
  endtask : run_phase

  task drive_txn(item_c#(NUM_LANE) _item);
    for (int ii=0; ii<NUM_LANE; ii++) begin
      // create a thread for each valid lane
      if (_item.vld[ii] == 1'b1) begin
        fork
          automatic int unsigned jj = ii; 
          begin
            this.vif.drv_cb.lane[jj].vld <= _item.vld;
            this.vif.drv_cb.lane[jj].dat <= _item.data;
            @this.vif.drv_cb;
            this.vif.drv_cb.lane[jj].vld <= '0; 
            this.vif.drv_cb.lane[jj].dat <= 'x; 
          end
        join_none
      end // if vld
    end // for num lanes
    wait fork;
  endtask : drive_txn

...

endclass : my_driver

In reply to jmlemay:

Your driver code doesn’t match your interface. Your driver has ‘vif.drv_cb.lane[jj]’, but in the interface, drv_cb doesn’t have any variable ‘lane’, only ‘vld’ and ‘data’.

Why are you creating individual threads? Why not just use a for loop to do assignments, wait on the clocking block, and then do another for loop?