Reputation: 171
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
Upvotes: 0
Views: 916
Reputation: 1482
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:
on the first posedge clock tick: shutdown_now will switch from 0 to 1.
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).
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.
Upvotes: 0
Reputation: 554
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:
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.
Upvotes: 0
Reputation: 19094
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.
Upvotes: 1