I have an exercise that separating comb logic from sequential logic in always_ff block.
However, I found that the ordering of always_comb and always_ff executions is different between different simulators.
As far as I know, always_comb block is executed whenever the variable in sensitivity list changed. (Please correct me if I am wrong) and always_ff is executed at signal edge (e.g. clk).
From the programming prospective, the execution ordering of always_comb and always_ff is important, right? We always assign a variable in non-blocking way inside flip flop from a variable which is driven by always_comb?
out_pkt_size <= pkt_size; // pkt_size is drive by always_comb
Please advise.
You can checkout the code from edaplayground
always_comb begin
$display("%2g, always_comb : in_vld = %b", $time, in_vld);
if (in_vld) begin
pkt[HIBIT-:WIDTH] = in_data;
pkt_size = {<<8{pkt[HIBIT-:16]}};
end else begin
pkt = {WIDTH{1'hx}};
pkt_size = 'd0;
end
end
always_ff @(posedge clk or negedge reset_n) begin
$strobe( "%2g, always_ff(strobe) : out_pkt_size = %1d", $time, out_pkt_size);
$display("%2g, always_ff : in_vld = %b", $time, in_vld);
if (!reset_n) begin
out_pkt_size <= 1'd0;
end else begin
out_pkt_size <= pkt_size;
end
end
Run result from edaplayground (using Synopsys VCS as simulator):
You can see that always_comb is executed earlier than the always_ff.
initial ...
0, always_ff : in_vld = x
0, always_comb : in_vld = x
0, always_ff(strobe) : out_pkt_size = 0
10, always_comb : in_vld = 0
10, always_ff : in_vld = 0
10, always_ff(strobe) : out_pkt_size = 0
20, always_comb : in_vld = 1
20, always_ff : in_vld = 1
20, always_ff(strobe) : out_pkt_size = 140
30, always_comb : in_vld = 0
30, always_ff : in_vld = 0
30, always_ff(strobe) : out_pkt_size = 0
40, always_ff : in_vld = 0
40, always_ff(strobe) : out_pkt_size = 0
50, always_ff : in_vld = 0
50, always_ff(strobe) : out_pkt_size = 0
$finish called from file "testbench.sv", line 96.
$finish at simulation time 55
Result from ModelSim:
You can see that, however, the always_ff is always executed earlier than the always_comb. It results the out_pkt_size is 1 clock cycle behind.
run
# initial ...
# 0, always_comb : in_vld = x
# 0, always_ff : in_vld = x
# 0, always_ff(strobe) : out_pkt_size = 0
# 10, always_ff : in_vld = 0
# 10, always_comb : in_vld = 0
# 10, always_ff(strobe) : out_pkt_size = 0
# 20, always_ff : in_vld = 1
# 20, always_comb : in_vld = 1
# 20, always_ff(strobe) : out_pkt_size = 0
# 30, always_ff : in_vld = 0
# 30, always_comb : in_vld = 0
# 30, always_ff(strobe) : out_pkt_size = 140
# 40, always_ff : in_vld = 0
# 40, always_ff(strobe) : out_pkt_size = 0
# 50, always_ff : in_vld = 0
run