I’ve always struggled with choosing the right (not necessarily the easiest) approach for synchronizing different procedural constructs within the testbench environment. The reason for this is the lack of knowledge on the simulator scheduling for SystemVerilog. I hope answering the following cases would increase my knowledge, and hence, my confidence in choosing the right approach.
Case 1 - default clocking block
CODE
module tb;
bit clk;
always #5ns clk = ~clk;
default clocking cb @(posedge clk);
endclocking
logic flag;
always_ff @(posedge clk) begin
$display("always_ff exec");
flag <= '1;
end
initial begin
##1; // @(cb);
$display(" ##1 executed - flag:%b", flag);
$stop;
end
endmodule
RESULT
run -all
always_ff exec
##1 executed - flag:1
** Note: $stop : testbench.sv(18)
Analysis and Questions: It seems like the code after ‘##1’ is executed after the NBA region, possibly in the observed region (?!) since using $display shows the updated value of the signal flag. Also, ‘##1’ is executed after the posedge clocking event (perhaps helps avoid race conditions (?!)). If this event gets executed in the Observed region, I had thought that there is no mechanism, within a time slot, go back to the active region. In what region is the $display command after the ‘##1’ statement executed?
Case 2 - ‘#’ delay execution vs edge execution
CODE
module tb;
bit clk;
always #5ns clk = ~clk;
always_ff @(posedge clk) $display("always_ff exec");
initial begin
#5ns;
$display(" #5ns executed");
#0; $stop;
end
endmodule
RESULT
run -all
#5ns executed
always_ff exec
** Note: $stop : testbench.sv(11)
Analysis and Questions: It seems like ‘5ns’ is executed first, even though its exactly on the time-slot where rising-edge of the clock takes place. I believe this delay event is executed in the active region (?!), and it seems to hold a higher precedence over the ‘@posedge clk’ event trigger which is also supposedly evaluated in the active region (?!). I’m not sure about how the scheduling works for this. The standard has information all over the place and I’m not fully able to relate the description there to this sort of example here.
Case 3 - covergroup sampling
CODE
module tb;
bit clk;
initial forever #5ns clk = ~clk;
logic flag;
always_ff @(clk) flag = 1;
covergroup cg @(posedge clk);
coverpoint flag {
bins b0 = {1};
}
endgroup
cg inst_cg = new();
initial begin
@(posedge clk);
$display($get_coverage, $time);
@(posedge clk);
$display($get_coverage, $time);
$stop;
end
endmodule
RESULT
run -all
0 5
100 15
** Note: $stop : testbench.sv(21)
Analysis and Questions: I’m not sure when the sampling the of the covergroups happen within the schedule. I would have imagine, the ‘always_ff’ block gets executed first, and that would immediately assign the signal flag to 1. After this, the @(posedge clk) within the covergroup gets executed and that would sample the signal flag, which by now should have the value of 1. Finally, the @(posedge clk) of the ‘initial block’ is executed and then that should have displayed the 100 as the total coverage. I’m not sure what exactly is the sequence of operations here but I’m mistaken or confused to what should happen.
I’ve tried to formulate a good question. I’ll be happy to add if anything else is required.
Thanks for the time and thanks in advance for the answers.