0

I am wondering about the behavior of the below code. There are two always blocks, one is combinational to calculate the next_state signal, the other is sequential which will perform some logic and determine whether or not to shutdown the system. It does this by setting the shutdown_now signal high and then calling state <= next_state.

My question is if the conditions become true that the shutdown_now signal is set (during clock cycle n) in a blocking manner before the state <= next_state line, will the state during clock cycle n+1 be SHUTDOWN or RUNNING? In other words, does the shutdown_now = 1'b1 line block across both state machines since the state signal is dependent on it through the next_state determination?

 enum {IDLE, RUNNING, SHUTDOWN} state, next_state;
 logic shutdown_now;

 // State machine (combinational)
 always_comb begin
    case (state)
       IDLE: next_state <= RUNNING;
       RUNNING: next_state <= shutdown_now ? SHUTDOWN : RUNNING;
       SHUTDOWN: next_state <= SHUTDOWN;
       default: next_state <= SHUTDOWN;
    endcase
 end

 // Sequential Behavior
 always_ff @ (posedge clk) begin
    // Some code here
    if (/*some condition*/) begin
       shutdown_now = 1'b0;
    end else begin
       shutdown_now = 1'b1;
    end
    state <= next_state;
 end
Greg
  • 18,111
  • 5
  • 46
  • 68
miles.sherman
  • 171
  • 3
  • 11

3 Answers3

1

First off, you are not following property coding. The always_comb should only use blocking (=) assignments, never non-blocking (<=). And always_ff is the reverse, only non-blocking (<=) assignments, never blocking (=).

With the code as is, state will go RUNNING. This is because the assignment to next_state is non-blocking and thereby next_state will not be updated until later in the scheduler.

Hypothetically, if next_state and shutdown_now were both blocking assignments, then the simulator will have a race condition. Both next_state could be evaluated and updated before or after state is evaluated. This is why it is not a good idea to mix blocking and non-blocking in the same always block.

If properly coded, ie next_state = ... and shutdown_now <= ..., then state will also go to RUNNING. This is because shutdown_now update happens after all scheduled evaluations are complete. So next_state will not see the 1'b1 until after state is evaluated.

Greg
  • 18,111
  • 5
  • 46
  • 68
  • Hi Greg, thank you for your response. I see the race condition you are describing and I will be cautious of it going forward. I do agree with you that the always_comb block should always be blocking. However, maybe I'm misunderstanding you when you say all assignments in an always_ff block should always be non-blocking? I often use both blocking and non-blocking statements in sequential blocks to create a combinational chain between flip-flops. Would you mind clarifying? – miles.sherman Jun 09 '14 at 16:20
  • 2
    @Miles, while I've known some people who do mix blocking and non-blocking statements in a sequential block. It's not something that I do. As Greg states, it is generally recommended not to mix the different assignment types in a process. I tend to code my combinatorial and sequential logic in their own (usually very small) processes. To me it seems to make the code easier to read and easier for the synthesis tools to handle. – Ciano Jun 09 '14 at 16:32
  • Stylistically, you are correct. However, there is nothing actually wrong with using non-blocking assignments in an always_comb block, or using blocking assignments in an always_ff block. The LRM defines a consistent behavior that all modern tools implement. – Jonathan Mayer Jun 25 '14 at 00:34
  • @JonathanMayer, blocking vs non-blocking still matters as the events within each region can be executed in an indeterminate order. The order that each region is executed is determinate. [IEEE1800-2012](http://standards.ieee.org/getieee/1800/download/1800-2012.pdf) sections 4.6 _Determinism_, 4.7 _Nondeterminism_, & 4.8 _Race conditions_. The LRM allows blocking and NBA in all forms of `always`, but if you want RTL, gates, & circuit to match, then follow the blocking/non-blocking guidelines. – Greg Jun 25 '14 at 05:14
0

Miles, you really only have one state machine, that's the code in your always_comb block. The always_ff just creates a register to hold your state.

The way your code is written now, you will perform a shutdown in the following sequence:

  • cycle n: the logic in your always_ff block will determine if a shutdown should happen, and will SCHEDULE the shutdown_now signal to assert in the next clock tick.
  • cycle n+1: shutdown_now asserts, and the state machine (currently in RUNNING) will set the next_state to SHUTDOWN.
  • cycle n+2: now your state machine will be in the SHUTDOWN state.

Not sure that you need to have your shutdown logic in an always_ff block. If you move that code to a always_comb block, you could leave the rest of your code the same and your state machine would move to the SHUTDOWN state in cycle n+1.

Ciano
  • 554
  • 5
  • 16
  • As Greg mentioned, you do need to fix your blocking/non-blocking assignments. – Ciano Jun 09 '14 at 16:05
  • Thanks Ciano, much appreciated. I do need the state to be in SHUTDOWN during cycle n+1. I think I should be able to move the shutdown logic into the always_comb block to ensure this behavior. Also, see my comment on Greg's post regarding the blocking/non-blocking assignments. – miles.sherman Jun 09 '14 at 16:23
0

Here is how I would expect that code to perform:

You have two registers: a "shutdown_now" register and a "state" register.

On posedge of clk, the state of both of these registers is updated atomically. That is: when a blocking assignment to shutdown_now takes place, the current process isn't interrupted while state_next gets updated. Instead, for the purposes of the always_ff process, state_next is whatever value it held at the beginning of the posedge clk simulation "tick."

So:

  1. on the first posedge clock tick: shutdown_now will switch from 0 to 1.

  2. Once the "posedge clk" process has completed, the "always_comb" block will notice that it has work to do, and will update state_next (ie. a combinational decode from the new state of the 2 registers).

  3. on the second posedge clock tick: state gets latched with the updated state_next.

So, it will take 2 cycles to shut down your system, not 1.

Jonathan Mayer
  • 1,432
  • 13
  • 17