这篇文章讲的问题是有限状态机的编码风格以及如何避免组合逻辑输出。这里主要记录一下摩尔状态机的处理。以下面的FSM为例
根据输出是否与输入有关,状态机可以分为摩尔型和米利型。它们的系统框图如下
文章提到的状态机编码风格也成为“三段式”,分为下面三个部分
- 状态寄存器(时序电路)
- 下一个状态产生(组合电路)
- 输出(组合电路)
上图提到的状态机在这种代码风格下就是这样的
module fsm(
input clk,
input rst_n,
input go,
input ws,
output rd,
output ds
);
reg [1:0] state,next_state;
parameter IDLE = 2'b00,
READ = 2'b01,
DLY = 2'b10,
DONE = 2'b11;
// PART1
always@(posedge clk,negedge rst_n)
if(!rst_n)
state <= IDLE;
else
state <= next_state;
// PART2
always@(*)begin
next_state = IDLE;
case(state)
IDLE: if(go==1'b1)
next_state = READ;
else
next_state = IDLE;
READ: next_state = DLY;
DLY: if(ws)
next_state = READ;
else
next_state = DONE;
DONE: next_state = IDLE;
default: next_state = IDLE;
end
// PART3
assign rd = (state==READ) || (state==DLY);
assign ds = (state==DONE);
endmodule
我认为这种编码风格的好处是可以将时序逻辑和组合逻辑分开,并且总体结构和FSM的结构是相对应的。初次之外还有个好处是有时候输出可能需要next_state
,这种结构的代码天然就有next_state
。
为了使状态机的输出为寄存器输出,文章提出了两个思路
- 在输出信号上再添加一层寄存器
- 通过编码状态直接使用状态寄存器的某些位作为输出
这里主要说一下第二种方法。还是以上面的状态机为例。首先将状态机的所有状态和输出做成下面的表格
State-Output | ds | rd |
---|---|---|
IDLE | 0 | 0 |
READ | 0 | 1 |
DLY | 0 | 1 |
DONE | 1 | 0 |
然后检查是否有相同的输出模式,这里READ和DLY是相同的,为了保证每个状态的编码的独立性,需要添加一列来进行区分,如下表
State-Output | ds | rd | |
---|---|---|---|
IDLE | 0 | 0 | 0 |
READ | 0 | 0 | 1 |
DLY | 1 | 0 | 1 |
DONE | 0 | 1 | 0 |
这样状态的编码如下
parameter IDLE = 3'b000,
READ = 3'b001,
DLY = 3'b101,
DONE = 3'b010;
对应的输出为
assign {ds,rd} = state[1:0];
这样就可以直接用状态寄存器来得到输出。至于为什么要尽量避免状态机的输出端有组合逻辑,在Synopsys Design Compiler的手册中有说明。