You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

针对iCE40构建时Yosys处理FSM的异常行为排查

解决SpinalHDL生成FSM在Yosys综合时的异常问题

针对你遇到的SpinalHDL生成的FSM在Yosys中无法正常工作的问题,我整理了几个可行的解决方案,同时解释问题的根源:

问题回顾

你发现只有给SpinalHDL生成的Verilog状态寄存器state添加(* fsm_encoding = "none" *)属性,代码才能按预期运行,但手动修改大量生成代码不现实,且不确定是否有Yosys配置可以替代这个操作。当前使用的Yosys版本是Yosys 0.9+3667 (git sha1 e7f36d01, gcc 7.3.0-27ubuntu1~18.04 -fPIC -Os),综合命令为:

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

有问题的Verilog代码如下:

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

解决方案1:修改Yosys综合命令,跳过自动FSM优化

Yosys的synth_ice40流程默认会自动检测并优化FSM编码(比如使用one-hot、Gray码等),但这种优化可能与SpinalHDL生成的FSM逻辑不兼容。你可以通过以下两种方式禁用或修改FSM处理:

方法A:使用-nofsm选项完全禁用FSM优化

在synth_ice40命令中添加-nofsm参数,让Yosys不对FSM做任何特殊处理,保持原始的状态寄存器逻辑:

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

方法B:全局指定FSM编码为none

如果不想完全禁用FSM处理,只是希望默认使用none编码,可以在综合前添加fsm -encoding none命令:

yosys -v2 -p "fsm -encoding none; synth_ice40 -top MyTopLevel -json MyTopLevel.json" MyTopLevel.v

解决方案2:让SpinalHDL自动生成fsm_encoding属性

从根源解决问题,让SpinalHDL在生成Verilog时自动给FSM状态寄存器添加属性,无需手动修改代码:

单个FSM添加属性

在SpinalHDL代码中,给状态寄存器调用addAttribute方法:

import spinal.core._
import spinal.lib._

class MyTopLevel extends Component {
  val io = new Bundle {
    val but0 = in Bool()
    val but1 = in Bool()
    val leds = out UInt(2 bits)
  }

  val counter = Reg(UInt(32 bits)) init 0
  val state = Reg(UInt(2 bits)) init 0
  // 给状态寄存器添加fsm_encoding属性
  state.addAttribute("fsm_encoding", "none")
  val leds = Reg(UInt(2 bits)) init 0
  io.leds := leds

  counter := counter + 1

  switch(state) {
    is(0) {
      when(io.but0 === False && io.but1 === False) {
        state := 2
        leds := 3
      }
    }
    is(1) {
      when(io.but0 === True && io.but1 === True) {
        state := 0
        leds := 0
      }
    }
    default {
      when(io.but0 === False && io.but1 === True) {
        state := 1
        leds := 1
      }
      when(io.but0 === True && io.but1 === False) {
        state := 1
        leds := 2
      }
    }
  }
}

全局给所有FSM添加属性

如果需要给项目中所有FSM状态寄存器自动添加属性,可以通过SpinalHDL的全局配置实现:

SpinalConfig(
  verilogAttributeProvider = (name, obj) => {
    obj match {
      // 根据寄存器名称判断是否为FSM状态寄存器(这里假设状态寄存器命名包含"state")
      case reg: Reg[_] if reg.getName().contains("state") =>
        List(VerilogAttribute("fsm_encoding", "none"))
      case _ => Nil
    }
  }
).generateVerilog(new MyTopLevel)

问题根源分析

Yosys的FSM优化模块会自动分析状态转移逻辑,尝试用更高效的编码减少资源占用,但你的代码中FSM的默认分支处理了两种独立的转移情况,这种非标准的转移结构可能导致Yosys的自动优化逻辑误判,破坏了原始的状态行为。通过禁用自动FSM优化或指定none编码,Yosys会保持原始的状态寄存器取值和转移逻辑,从而解决问题。

内容的提问来源于stack exchange,提问作者pointcheck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:27:24