Why are we driving the read data after two clocks in driver and monitor of a simple memory design?

Design spec: https://verificationguide.com/systemverilog-examples/systemverilog-testbench-example-memory_m/
EDA Code: UVM TestBench Example code - verificationguide.com - EDA Playground

The design spec says, “Read Operation: address and rd_en should be driven on the same clock cycle, Design will respond with the data in the next clock cycle.”

But in the UVM monitor and driver codes under the rd_en conditions, we, respectively, are sampling and driving the memory’s read data after waiting on two of posedge clocks. Why is that?

Is there anything that I am missing here?

In reply to brain_storm:

Both the driver and monitor are poorly written and using extra clocks to mask functional issues.

When you are using clocking blocks, everything should be sampled in relation to the clocking block. In this testbench, the driver and monitor are mixing the top interface clock and the clocking block data values.

Here is a better monitor:


  virtual task run_phase(uvm_phase phase);
    forever begin
      @(vif.monitor_cb);
      if(vif.monitor_cb.wr_en) begin
        trans_collected = new();
        trans_collected.addr = vif.monitor_cb.addr;
        trans_collected.wr_en = vif.monitor_cb.wr_en;
        trans_collected.wdata = vif.monitor_cb.wdata;
        trans_collected.rd_en = 0;
        item_collected_port.write(trans_collected);
      end
      if(vif.monitor_cb.rd_en) begin
        trans_collected = new();
        trans_collected.addr = vif.monitor_cb.addr;
        trans_collected.rd_en = vif.monitor_cb.rd_en;
        trans_collected.wr_en = 0;
        @(vif.monitor_cb);
        trans_collected.rdata = vif.monitor_cb.rdata;
        item_collected_port.write(trans_collected);
      end
    end 
  endtask : run_phase