So, i was making a state machine in a certain verilog design for Altera's EP1C3 Cyclone FPGA, in their "brand spanking new" Quartus II 11.0 (for whatever reason, they dropped the support for older FPGAs in newer versions, but i digress).
This is something that should be fairly straightforward, right?
Turns out it isn't.
Here is the outline of the code in question.
parameter ST_IDLE =0;
parameter ST_WR_WORD =1;
parameter ST_WR_WORD_1 =2;
parameter ST_WR_WORD_2 =3;
parameter ST_WR_WORD_3 =4;
parameter ST_WR_WORD_4 =5;
parameter ST_WR_WORD_5 =6;
parameter ST_RX =7;
parameter ST_RX_1 =8;
parameter ST_RX_2 =9;
parameter ST_TX =10;
parameter ST_TX_1 =11;
parameter ST_TX_2 =12;
reg [3:0] state;
reg [3:0] next_state;
reg [15:0] to_write;
...
always @(posedge clk)
begin
if (reset) begin
state <= ST_IDLE;
next_state <= ST_IDLE;
...
end else begin
case (state)
ST_IDLE: begin
...
if (rd) begin
state <= ST_WR_WORD;
next_state <= ST_RX;
to_write <= 16'h0000;
end else if (wr) begin
state <= ST_WR_WORD;
next_state <= ST_TX;
to_write <= 16'h0000;
end
end
...
ST_RX: begin
state <= ST_WR_WORD;
next_state <= ST_RX_1;
to_write <= 16'hB000;
end
...
ST_TX: begin
state <= ST_WR_WORD;
next_state <= ST_TX_1;
to_write <= 16'hB800;
end
...
ST_WR_WORD: begin
...
state <= ST_WR_WORD_1;
end
...
ST_WR_WORD_5: begin
...
state <= next_state;
end
endcase
end
end
The defining characteristic of it is that the current state can be assigned from another register, which stores the next step to be taken after a "subroutine" set of states.
Just so you won't need to retype the same code every time you need to clock a word out.
Well, it didn't work.
Even worse, not only it didn't work, it didn't work in weird and wonderful ways, getting stuck in states that are by all means impossible.
I was scratching my head for some time (
that is), and eventually decided to memory-map the state register to track what is going on.
Now, as we all know, the FSM state values are usually optimized away by the synthesizer, being replaced by something more efficient, like one-hot encoding.
To see what is going on, you need to explicitly forbid such an optimization.
Like so:
(* syn_encoding = "user" *) reg [3:0] state;
(* syn_encoding = "user" *) reg [3:0] next_state;
And guess what?
Once i did that it started to work perfectly.
As best as i can explain it, and judging by some clues in the report files, Quartus optimized "state" one way, and "next_state" the other way, causing random state changes when former is assigned with the latter.
So, the questions arise:
1. Is that a normal way of making such an FSM, or have i done something quite simply wrong in designing it?
2. Should the design software know better than screw up like that, or is this some sort of a limitation one should have been aware of?