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
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!