Electronics > FPGA

Altera's shenanigans with a state machine, or me failing at verilog?

(1/1)

Artlav:
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.


--- Code: ---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

--- End code ---

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 ( |O 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:

--- Code: ---(* syn_encoding = "user" *) reg [3:0] state;
(* syn_encoding = "user" *) reg [3:0] next_state;

--- End code ---

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.  :palm:

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?

marshallh:
That's typical, and my code looks similar. You shouldn't have to put synthesis directives on your FSMs. The fact that you forced it to 1-hot and it started working points to another problem.
What are your other inputs here? Are they synchronized with the local clock? Have you constrained your main clock?

FSM encoding should generally be left at Auto in the Synthesis settings. You can also use the syn_preserve to keep your FSMs untouched when tapping with an external LA>

I would suggest using SignalTap. You can either tap the [3:0] register, or use Add State Machines... where it lets you see the FSM state regardless of the optimizations in the synthesis stage.

See the PDF in my signature.

marshallh:
Also, versions 10 and 11 of Quartus are flaming POSes, I suggest you use 9.1SP2 for that chip. Or use a Cyclone II and use up to 13.0sp1..

Artlav:

--- Quote from: marshallh on November 29, 2014, 12:43:28 am ---The fact that you forced it to 1-hot and it started working points to another problem.

What are your other inputs here? Are they synchronized with the local clock? Have you constrained your main clock?
--- End quote ---
Quite to the contrary, it WAS 1-hot when the problem was happening, and i forced it to "don't optimize", in other words, another version of syn_preserve.
Prior implementations with only one state and typed-out word sending every time it needed was working, without any directives.

The inputs are all synchronised - there isn't much more to it than in the outline, it takes input from the CPU, and drives an SPI bus.
Clocks are constrained.

Navigation

[0] Message Index

There was an error while thanking
Thanking...
Go to full version