Moore机编码风格

考虑以上状态机。
我们使用Chisel中的Enum
来表示状态,使用switch
来描述状态转移。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import chisel3._ import chisel3.util._
class SimpleFsm extends Module { val io = IO(new Bundle { val badEvent = Input(Bool()) val clear = Input(Bool()) val ringBell = Output(Bool()) }) val green :: orange :: red :: Nil = Enum(3) val stateReg = RegInit(green) switch(stateReg) { is(green) { when(io.badEvent) { stateReg := orange } } is(orange) { when(io.badEvent) { stateReg := red } } is(red) { when(io.clear) { stateReg := green } } } io.ringBell := stateReg === red }
|
需要注意到,和Verilog不同的是,在实现状态机时,我们并没有在代码中引入next_state
这个信号,也就是说,不同于我们在Verilog中描述的三段式/二段式状态机。
其原因大抵是因为,Verilog不允许在同一个过程块中,对组合逻辑和时序逻辑进行同时的赋值更新,而Chisel是允许的。
在后面的Mealy机编码中,我们可以看到这样实现的好处。
Mealy机编码风格
一个最简单的上升沿检测机制,可以使用一行Chisel代码实现:
1
| val risingEdge = din & !RegNext(din)
|
其综合出的电路如图所示:

对于一个上升沿检测,如果我们使用状态机进行表示,其状态转移图如图所示:

由于是Mealy状态机,所以状态机的输出也和输入有关。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import chisel3._ import chisel3.util._
class RisingFsm extends Module { val io = IO(new Bundle{ val din = Input(Bool()) val risingEdge = Output(Bool()) }) val zero :: one :: Nil = Enum(2) val stateReg = RegInit(zero) io.risingEdge := false.B switch (stateReg) { is(zero) { when(io.din) { stateReg := one io.risingEdge := true.B } } is(one) { when(!io.din) { stateReg := zero io.risingEdge := false.B } } } }
|
在上述代码中,我们可以看到,和Verilog编码风格完全不同,时序逻辑和组合逻辑在同一个Block(块)中进行了赋值,组合逻辑将立即发生跳变,而时序逻辑将在下一个时钟上升沿更新,刚刚从Verilog转过来的设计者可能不太适应这种风格。
或许我们应该转变思路,应该将 stateReg := one
这样的代码看作是次态转移逻辑,而不是对现态寄存器的更新,此时:
猜测这种写法的设计哲学是,将次态转移和输出逻辑写在了同一个块中,方便维护。