Strange behaviour of yosys on FSM while building for iCE40

414 Views Asked by At

I have been toying with SpinalHDL and its SoCs and modules, but came to a general problem - almost none of the code that uses FSMs works. I nailed this issue down to Yosys, which does something weird when it detects a FSM. Here below is a simple and straightforward Verilog (generated by SpinalHDL) that does NOT work as expected unless you pepend FSM state register "state" with ( fsm_encoding = "none" ) attribute. Doing so makes it working well, but that is definitely not a solution because you cannot modify tonns of existing Verilog, neither I could find a way to make SpinalHDL do this. I discussed this issue on SpinalHDL gitter already, the guys there advised to file a bug report on Yosys which I think is not the case, I think I'm missing some option.

I'm using the latest Yosys from github:

Yosys 0.9+3667 (git sha1 e7f36d01, gcc 7.3.0-27ubuntu1~18.04 -fPIC -Os)

Verilog that does not work:

// Generator : SpinalHDL v1.4.2    git head : 804c7bd7b7feaddcc1d25ecef6c208fd5f776f79
// Component : MyTopLevel



module MyTopLevel (
  input               io_but0,
  input               io_but1,
  output     [1:0]    io_leds,
  input               clk,
  input               reset
);
  reg        [31:0]   counter;
  //(* fsm_encoding = "none" *) reg        [1:0]    state;
  reg        [1:0]    state;
  reg        [1:0]    leds;

  assign io_leds = leds;
  always @ (posedge clk) begin
    if(reset) begin
      counter <= 32'h0;
      state <= 2'b00;
      leds <= 2'b00;
    end else begin
      counter <= (counter + 32'h00000001);
      if((state == 2'b00))begin
        if(((io_but0 == 1'b0) && (io_but1 == 1'b0)))begin
          state <= 2'b10;
          leds[0] <= 1'b1;
          leds[1] <= 1'b1;
        end
      end else begin
        if((state == 2'b01))begin
          if(((io_but0 == 1'b1) && (io_but1 == 1'b1)))begin
            state <= 2'b00;
            leds[0] <= 1'b0;
            leds[1] <= 1'b0;
          end
        end else begin
          if(((io_but0 == 1'b0) && (io_but1 == 1'b1)))begin
            state <= 2'b01;
            leds[0] <= 1'b1;
            leds[1] <= 1'b0;
          end
          if(((io_but0 == 1'b1) && (io_but1 == 1'b0)))begin
            state <= 2'b01;
            leds[0] <= 1'b0;
            leds[1] <= 1'b1;
          end
        end
      end
    end
  end


endmodule

I'm using the following command to invoke Yosys from my Makefile:

yosys -v2 -p "synth_ice40 -top MyTopLevel -json MyTopLevel.json" MyTopLevel.v
1

There are 1 best solutions below

4
On

Well, I have found the root of the problem, but cannot find a decent solution.

Yosys indeed does FSM recoding (with synth_ice40 command) without any permission to do so, it converts FSM state register into one-hot which is zero initialized during boot (FPGA configuration) and so we get to invalid FSM state on power-on. There are three solutions here, as it seems to me: 1) do not rely on FPGA reset, implement my own hard reset instead, 2) prepend each FSM state register with fsm_encoding none attribute, 3) assign initial state reg value in Verilog - after this yosys stops doing its nasty FSM tricks. Unfortunately none of these are good for me. I do really want to rely on FPGA reset and I do not want to modify someone else's code (SpinalHDL framework) just to please yosys. According to yosys manual one can disable FSM recoding with fsm -recode option, but that does not work when you use a synth_ command - it appeared to be a hard-coded script that overrides whatever you give it on the command line. Can this be considered as a bug in yosys ? I think so!