0
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ That's the point with hardware description languages -- the correct functioning of your code should not depend on the order of execution of blocks in a simulator. When you build the actual hardware, everything happens in parallel anyway! \$\endgroup\$ – Dave Tweed Aug 4 '18 at 10:59
  • \$\begingroup\$ The order of execution of different 'always' blocks is not defined, that's why you must use nonblocking assignments for flip-flops to get the desired behavior. You must stop thinking about hardware design from a "programming perspective". \$\endgroup\$ – Elliot Alderson Aug 4 '18 at 13:35
1
\$\begingroup\$

The difference in ordering between your always_ff and always_comb is a result of different assignment ordering between from your initial and always blocks.

Your initial block uses a #10 delay to make assignments to inputs sensitive in the always_comb block. That coincides with the assignment clk=~clk in the always block. So you have a race condition with the timing of the always_ff's positive edge of clk and the inputs to the always_comb

You need to modify the timing of initial block assignment to not be in a race by using non-blocking assignments, or using delays that put the assignments off from the posedge of the clock.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.