Effect of clocking block skew on combinational logic

I updated my UVM TB for up_down_counter for clocking block and I see below issue with non-zero skew value:
RTL code is:


module up_down_counter(
  input clk,   
  input run_counter, // clk_en
  input rst_n,  // Asynchronous reset active low
  input [6:0] count_value, //max_count
  input load_counter,
  input up_counter,  // up/down counter mode
  output logic [6:0] current_value,
  output logic count_reached
);
  logic [7:0] count;
:
  assign count_reached = up_counter ? count == count_value - 1: count[7];
:
:

‘count’ is loaded with 0 or count_value when load_counter is true(based on up or down counter mode) and is incremented/decremented every clk_en.
‘count_reached’ is generated combinationally as shown above. The problem is, when load_counter is set (with non-zero skew), count_reached changes immediately(if counter mode or count_values are different). This causes mismatch against expected count_reached in scoreboard for that clk.
I believe, this tend to happen with comb logic and don’t see any solution on TB side to handle it. Just wanted to check, if there is any way TB could handle this?
Please let me know.

In reply to uvmsd:

To be honest this is not enough code to answer your question.

In reply to chr_sue:
@chr_sue
Just providing more of relevant code:
Interface


interface updn_counter_if(input logic clk, reset, clk_en);
  logic [ABSOLUTE_DATA_WIDTH-1:0] count_value;
  logic                           load_counter;
  logic                           up_counter;
  logic [ABSOLUTE_DATA_WIDTH-1:0] current_value;
  logic                           count_reached;
  logic                           run_counter;

clocking counter_cb @(posedge clk);
  //non-zero skew causes mismatch on scoreboard since 
  // count_reached is a comb logic.
  //default input #1step output #1;
 default input #0 output #0;
  inout load_counter;
  inout count_value;
  inout up_counter;
  inout run_counter;
  input current_value;
  input count_reached;  
endclocking
endinterface

Monitor

virtual task run_phase(uvm_phase phase);
  super.run_phase(phase);
   wait(counter_vif.reset == 1);
  fork 
    forever begin
      @(counter_vif.counter_cb); 
      if (counter_vif.reset == 1) begin 
        updn_counter_seq_item seq_item_collected =new;
        seq_item_collected.count_value   = counter_vif.count_value;
        seq_item_collected.load_counter  = counter_vif.load_counter;
        seq_item_collected.up_counter    = counter_vif.up_counter;
        seq_item_collected.run_counter   = counter_vif.run_counter;
        items.push_back(seq_item_collected);
      end //if reset
    end //forever
    forever begin
      @(counter_vif.counter_cb); 
      if (counter_vif.reset == 1) begin
        if(items.size() > 0) begin
          updn_counter_seq_item seq_item_collected = items.pop_front();  
          seq_item_collected.current_value = counter_vif.counter_cb.current_value;
          seq_item_collected.count_reached = counter_vif.counter_cb.count_reached;
          //TLM Fifo
          trans_collected_port.write(seq_item_collected);
        end // item.size
      end //reset  
    end //forever
  join //fork   

scoreboard

 virtual task run_phase(uvm_phase phase);
    updn_counter_seq_item counter_pkt;
    forever begin
      wait(mon_pkt_qu.size() > 0);
      counter_pkt = mon_pkt_qu.pop_front();
      if(counter_pkt.load_counter) begin //load_counter =1
         :
        // load the local counter based on up/down counter
         :
      end //if load_counter
      else begin //load_counter =0
        if(scb_count_reached ) begin // count_reached == 1
           :
           // load the local counter based on up/down counter
            :
        end // //count_reached
        else begin //, no count_reached
          if(counter_pkt.up_counter) begin//up_counter mode
            if(counter_pkt.run_counter )
              scb_count = scb_count + 1;
          end  
          else begin //down_counter
            if(counter_pkt.run_counter) begin
              scb_count = scb_count - 1;
            end  //run_counter
          end //down_counter  
        end// no count_reached
      end //if no load_counter

      if ((counter_pkt.up_counter && (scb_count == counter_pkt.count_value -1)) || (!counter_pkt.up_counter && scb_count == 8'h00))
        scb_count_reached = 1'b1;
      else 
        scb_count_reached = 1'b0;
       :
       :
       //compare logic
       :
       :
  endtask
      

count_reached generation is a combinational logic in RTL. Any non-zero skew would set it immediately(i.e.not at the clock edge) when load_counter is true in the middle of counting(count_value can change with load_counter). Scoreboard cannot predict this and hence the mismatch.
Hope, its clear now. Sorry, if the info was incomplete earlier.

In reply to uvmsd:

Your problem is with respect to your clocking block. First, #0 is not recommended because it is progressing only a step in the SV scheduler.
In the monitor you are considering the cb in in some other parts not (review the monitor code).
In your interface the compiler should complain about the inpout based on logic.

In reply to chr_sue:

@chr_sue:
1. if you see, there is a commented line with non-zero delays in clocking block. Since it was causing the above stated problem, i ran the test with #0 and things are good with that. The comb logic in RTL is causing mismatch with non-zero delays in cb. That is my main intention of this question.

2. And about the monitor, I have used
@(counter_vif.counter_cb); in both the threads. Not sure which part you have mentioned about.
My driver is as below:
virtual task drive();
//drive with the clock
@( counter_vif.counter_cb);
if (counter_vif.reset ==1) begin
counter_vif.counter_cb.load_counter <= req.load_counter;
counter_vif.counter_cb.count_value <= req.count_value;
counter_vif.counter_cb.up_counter <= req.up_counter;
// pass clk_en as run_counter
counter_vif.counter_cb.run_counter <= counter_vif.clk_en;
end //if reset
endtask

3. Compiler didn’t throw any error for above case.

Please let me know if i am missing something.

In reply to uvmsd:

What I mean are lines of code like this:

if (counter_vif.reset ==1) begin

should be

if (counter_vif.counter_cb.reset ==1) begin
counter_vif.counter_cb.run_counter <= counter_vif.clk_en;

should be

counter_vif.counter_cb.run_counter <= counter_vif.counter_cb.clk_en;

In reply to chr_sue:

In reply to uvmsd:
What I mean are lines of code like this:

if (counter_vif.reset ==1) begin

should be

if (counter_vif.counter_cb.reset ==1) begin
counter_vif.counter_cb.run_counter <= counter_vif.clk_en;

should be

counter_vif.counter_cb.run_counter <= counter_vif.counter_cb.clk_en;

Thanks chr_sue for pointing them out. Since, our design uses different clocks, i accessed reset as vif.reset. is it really necessary to pass all the inputs through clocking_block? In that case, i can pass reset through fastest clock clocking block(clocks are in sync though).
So far, I have just used initial reset and that’s the reason didn’t face any issue with this.

clk_en is like another slower clock here. So, i didn’t pass it through clocking_block. Should we do that? In that, i may have to review some other code too.

In reply to uvmsd:

Whan you are using clocking blocks you have to work with the clockvars only.
BTW a signal may be in different clocking blocks.

In reply to chr_sue:

@chr_sue,

I get your point. My point was, since we assign other variables directly from req(transaction) as below (on right hand side):
counter_vif.counter_cb.load_counter <= req.load_counter;
counter_vif.counter_cb.count_value <= req.count_value;
Thought,

counter_vif.counter_cb.run_counter <= counter_vif.clk_en;

is also valid.

Anyway i updated my TB code now to include clockvars. And I still see the same behavior and its mainly because of comb logic in the RTL.

When there is a non-zero input skew and if count_value or counter mode(up_counter bit) change, mismatch is reported only on those boundary clocks.

assign count_reached = up_counter ? count == count_value - 1: count[7];

And one more thing, if we have multiple clocking blocks, do we have corresponding clockVar for reset for all the clocking blocks? Because, we may have to refer to reset with any of the clocking block.

Thank you!