有限状态机可以认为是组合逻辑和寄存器逻辑的特殊组合,他一般包括组合逻辑和寄存器逻辑两个部分,寄存器用于存储状态,组合逻辑用于状态译码和产生输出信号。
根据输出信号产生方法不同,状态机可分为两类:摩尔型(Moore)和米里型(Mealy)
米里型状态机的输出不但取决于状态还取决于输入,即下一个状态=F(当前状态,输入信号);输出信号=G(当前状态,输入信号),电路结构如下图所示。
摩尔型状态机的输出只取决于当前状态,且只在时钟边沿到来时才会有状态变化。即下一个状态=F(当前状态,输入信号);输出信号=G(当前状态),电路结构如下图所示。
在设计高速电路时,常常有必要使状态机的输出与时钟几乎完全同步。有一个办法是把状态变量直接用作输出,这种设计思路,在高速状态机电路时常常使用,这称为输出编码的状态指定。这种状态机也属于摩尔状态机。另一个方法是在输出逻辑G后面再加一组与时钟同步的寄存器输出流水线寄存器,让G所有的输出信号在下一个时钟跳变沿时同时存入寄存器组,即完全同步输出,这种输出称为流水线化的输出。如下图所示:
把状态机精确地分为哪几类,其实并不重要,重要的是设计者如何把握输出的结构能满足设计的整体目标,包括定时的准确性和灵活性。
在状态机设计中主要包含以下三个对象:
再用Verilog描述有限状态机时,有以下几种描述方式:
(1)单段式描述(CS+NS+OL):在单段式描述方式中,将状态机的现态、次态和输出逻辑放在一个always过程中进行描述,这样做相当于采用时钟信号来同步输出信号。因此,可以克服输出逻辑信号出现毛刺的问题,从而产生错误的控制逻辑问题。但这样的写法不利于维护,如果状态复杂一些就很容易出错,所以这种写法一般不太推荐。
(2)两段式描述(CS、NS+OL双过程描述):一个过程描述现态(CS),另一个过程描述次态和输出逻辑(NS+OL)。他把时序逻辑和组合逻辑划分开来,时序逻辑里进行当前状态和下一状态的切换,组合逻辑实现各输入、输出以及状态判断;这种写法相对容易维护,不过组合逻辑输出较易出现毛刺等现像。
(3)两段式描述(CS+NS、OL双过程描述):使用两个always过程来描述有限状态机,一个过程描述现态和次态时序逻辑(CS+NS),另一个过程描述输出逻辑(OL)。他的第一段采用时序逻辑描述状态的判断和切换。第二段也采用时序逻辑描述数据输出。这种新的写法在现在不同综合器中都可以被识别出来,这样既消除了组合逻辑可能产生的毛刺,又减小了代码量。
(4)三段式描述(CS、NS、OL):即现态(CS)、次态(NS)、输出逻辑(OL)各用一个always过程描述。即第一段采用时序逻辑描述当前状态和下一状态的切换,第二段用组合逻辑描述状态判断,第三段采用时序逻辑描述输出。三段式状态机的写法也是比较推荐的一种写法,代码容易维护,时序逻辑的输出解决了两段式写法中组合逻辑的毛刺问题。
在设计状态机时应尽量遵守以下原则:
1.给状态机的输出分配默认值,防止综合器产生不必要的锁存器。
2.将状态机的逻辑和所有的算数逻辑功能以及数据路径分离,包括与状态机输出值的分配分离,尽量使用多进程来描述状态机的原因。
3.如果设计中包含一个在对多个状态都要使用的运算,那么在状态机外面定义这个运算,然后让状态机的输出逻辑来使用该运算结果。
4.使用简单的同步或异步复位来确保状态机定义了一个上电初始状态。
常用编码方式:
1.顺序编码(Sequential State Machine Encoding)
顺序编码采用顺序的二进制数编码的每个状态。比如。如果有四个4个状态分别为state0、state1、state2、state3 和 state4。其二进制编码每个状态所对应的码字为00、01、10和11。顺序编码的缺点是在从一个状态转换到相邻状态时,有可能有多个比特同时发生变化,瞬变次数多,容易产生毛刺,引发逻辑错误。
2.格雷编码(Gray Code)
如果将state0、state1、state2 和 state3 4个状态编码为00、01、11和10,即为格雷编码方式。格雷码节省逻辑单元,而且在状态的顺序转换中,相邻状态每次只有一个比特位产生变化,这样既减少了瞬变得次数,也减少了产生毛刺和一些暂态得可能。
3.约翰逊编码(Johnson State Machine Encoding)
在约翰逊计数器得基础上引出约翰逊编码,约翰逊计数器是一种移位计数器,采用得是把输出得最高位取反,反馈送到最低位触发器得输入端。约翰逊编码每相邻两个字码间也是只有1个比特位不同。如果有6个状态state0 ~ state5,用约翰逊编码则为000、001、011、111、110和100。
4.一位独热编码(One-Hot Encoding)
一位热码是采用n位(或个n触发器)来编码具有n个状态得状态机。比如,对于state0、state1、state2 和 state3 4个状态可用码字1000、0100、0010和0001来代表。采用一位独热码,虽然多用了触发器,但可以有效节省和简化译码电路。对于FPGA器件来说,采用一位热码编码可有效提高电路的速度和可靠性,也有利于提高器件资源的利用率。
状态编码的定义:
在Verilog中,可用来定义状态编码的语句有parameter、`define、localparam。
1)用paramater参数定义
parameter state0=4'b0001, state1=4'b0010,
state2=4'b0100, state3=4'b1000;
...
/*调用*/
case(state)
state0: ...;
state1: ...;
...
2)用`define语句定义
·define state0 2‘b0001 /*不要加分号“;”*/
·define state1 2‘b0010
·define state2 2‘b0100
·define state3 2‘b1000
...
/*调用 */
case(state)
`state0: ...; //不要漏掉符号 “`”
`state1: ...;
...
3)用localparam定义
localparam用于定义局部参数,localparam定义的参数作用的范围仅限于本模块内,不可用于参数传递。由于状态编码一般只作用于本模块内,不需要被上层模块重新定义,因此localparam语句很适合状态机参数的定义。
localparam state0=4'b0001, state1=4'b0010,
state2=4'b0100, state3=4'b1000;
...
/*调用*/
case(state)
state0: ...;
state1: ...;
...
关键字 `define,parameter,localparam都可以用于定义参数和常量,但三者用法及作用的范围有区别,在于:
(1)`define: 其作用的范围可以是整个工程,能够跨模块(module),就是说在一个模块定义的`define指令,可以被其他模块调用,直到遇到`undef失效,所以用`define定义常量和参数时,一般习惯将定义模块放在模块外。
(2)parameter: 通常作用于本模块内,可用于参数传递,即可被上层模块重新定义,有三种参数传递方式:通过#(参数)传递;使用defparam语句显式重新定义;在Verilog-2001标准中还可以在线显式重新定义。
(3)localparam: 局部参数,不可用于参数传递。
以“01110001”序列检测器为例,要求检测输入信号A是否满足01110001序列,当信号满足该序列,给出Flag。“01110001”序列检测器的状态转化图如下所示:
“01110001”序列检测器单段式描述(CS+NS+OL)
module fsm_seq01110001(Clk,Rst_n,A,Flag);
input wire Clk;
input wire Rst_n;
input wire A;
output reg Flag;
/*状态编码,采用独热码*/
parameter S0 = 9'b000000001;
parameter S1 = 9'b000000010;
parameter S2 = 9'b000000100;
parameter S3 = 9'b000001000;
parameter S4 = 9'b000010000;
parameter S5 = 9'b000100000;
parameter S6 = 9'b001000000;
parameter S7 = 9'b010000000;
parameter S8 = 9'b100000000;
reg [8:0] state;
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
begin
state <= S0;
Flag <= 1'b0;
end
else
begin
case(state)
S0: if(A==1'b1)
begin
state <= S0;
Flag <= 1'b0;
end
else
begin
state <= S1;
Flag <= 1'b0;
end
S1: if(A==1'b1)
begin
state <= S2;
Flag <= 1'b0;
end
else
begin
state <= S1;
Flag <= 1'b0;
end
S2: if(A==1'b1)
begin
state <= S3;
Flag <= 1'b0;
end
else
begin
state <= S1;
Flag <= 1'b0;
end
S3: if(A==1'b1)
begin
state <= S4;
Flag <= 1'b0;
end
else
begin
state <= S1;
Flag <= 1'b0;
end
S4: if(A==1'b1)
begin
state <= S0;
Flag <= 1'b0;
end
else
begin
state <= S5;
Flag <= 1'b0;
end
S5: if(A==1'b1)
begin
state <= S2;
Flag <= 1'b0;
end
else
begin
state <= S6;
Flag <= 1'b0;
end
S6: if(A==1'b1)
begin
state <= S2;
Flag <= 1'b0;
end
else
begin
state <= S7;
Flag <= 1'b0;
end
S7: if(A==1'b1)
begin
state <= S8;
Flag <= 1'b0;
end
else
begin
state <= S2;
Flag <= 1'b1;
end
S8: if(A==1'b1)
begin
state <= S3;
Flag <= 1'b1;
end
else
begin
state <= S1;
Flag <= 1'b1;
end
default:begin
state <= S0;
Flag <= 1'b0;
end
endcase
end
end
endmodule
“01110001”序列检测器两段式描述(CS、NS+OL)
module fsm_seq01110001(Clk,Rst_n,A,Flag);
input wire Clk;
input wire Rst_n;
input wire A;
output reg Flag;
/*状态编码,采用独热码*/
parameter S0 = 9'b000000001;
parameter S1 = 9'b000000010;
parameter S2 = 9'b000000100;
parameter S3 = 9'b000001000;
parameter S4 = 9'b000010000;
parameter S5 = 9'b000100000;
parameter S6 = 9'b001000000;
parameter S7 = 9'b010000000;
parameter S8 = 9'b100000000;
reg [8:0] state;
reg [8:0] state_next;
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
state <= S0;
else
state <= state_next;
end
always@(*)
begin
if(!Rst_n)
Flag <= 1'b0;
else
begin
case(state)
S0: if(A==1'b1)
begin
state_next <= S0;
Flag <= 1'b0;
end
else
begin
state_next <= S1;
Flag <= 1'b0;
end
S1: if(A==1'b1)
begin
state_next <= S2;
Flag <= 1'b0;
end
else
begin
state_next <= S1;
Flag <= 1'b0;
end
S2: if(A==1'b1)
begin
state_next <= S3;
Flag <= 1'b0;
end
else
begin
state_next <= S1;
Flag <= 1'b0;
end
S3: if(A==1'b1)
begin
state_next <= S4;
Flag <= 1'b0;
end
else
begin
state_next <= S1;
Flag <= 1'b0;
end
S4: if(A==1'b1)
begin
state_next <= S0;
Flag <= 1'b0;
end
else
begin
state_next <= S5;
Flag <= 1'b0;
end
S5: if(A==1'b1)
begin
state_next <= S2;
Flag <= 1'b0;
end
else
begin
state_next <= S6;
Flag <= 1'b0;
end
S6: if(A==1'b1)
begin
state_next <= S2;
Flag <= 1'b0;
end
else
begin
state_next <= S7;
Flag <= 1'b0;
end
S7: if(A==1'b1)
begin
state_next <= S8;
Flag <= 1'b0;
end
else
begin
state_next <= S2;
Flag <= 1'b1;
end
S8: if(A==1'b1)
begin
state_next <= S3;
Flag <= 1'b1;
end
else
begin
state_next <= S1;
Flag <= 1'b1;
end
default:begin
state_next <= S0;
Flag <= 1'b0;
end
endcase
end
end
endmodule
“01110001”序列检测器两段式描述(CS+NS、OL)
module fsm_seq01110001(Clk,Rst_n,A,Flag);
input wire Clk;
input wire Rst_n;
input wire A;
output reg Flag;
/*状态编码,采用独热码*/
parameter S0 = 9'b000000001;
parameter S1 = 9'b000000010;
parameter S2 = 9'b000000100;
parameter S3 = 9'b000001000;
parameter S4 = 9'b000010000;
parameter S5 = 9'b000100000;
parameter S6 = 9'b001000000;
parameter S7 = 9'b010000000;
parameter S8 = 9'b100000000;
reg [8:0] state;
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
begin
state <= S0;
end
else
begin
case(state)
S0: if(A==1'b1)
state <= S0;
else
state <= S1;
S1: if(A==1'b1)
state <= S2;
else
state <= S1;
S2: if(A==1'b1)
state <= S3;
else
state <= S1;
S3: if(A==1'b1)
state <= S4;
else
state <= S1;
S4: if(A==1'b1)
state <= S0;
else
state <= S5;
S5: if(A==1'b1)
state <= S2;
else
state <= S6;
S6: if(A==1'b1)
state <= S2;
else
state <= S7;
S7: if(A==1'b1)
state <= S8;
else
state <= S2;
S8: if(A==1'b1)
state <= S3;
else
state <= S1;
default: state <= S0;
endcase
end
end
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
Flag <= 1'b0;
else if(state == S8)
Flag <= 1'b1;
else
Flag <= 1'b0;
end
endmodule
“01110001”序列检测器三段式描述(CS、NS、OL)
module fsm_seq01110001(Clk,Rst_n,A,Flag);
input wire Clk;
input wire Rst_n;
input wire A;
output reg Flag;
/*状态编码,采用独热码*/
parameter S0 = 9'b000000001;
parameter S1 = 9'b000000010;
parameter S2 = 9'b000000100;
parameter S3 = 9'b000001000;
parameter S4 = 9'b000010000;
parameter S5 = 9'b000100000;
parameter S6 = 9'b001000000;
parameter S7 = 9'b010000000;
parameter S8 = 9'b100000000;
reg [8:0] state;
reg [8:0] state_next;
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
state <= S0;
else
state <= state_next;
end
always@(state,A)
begin
case(state)
S0: if(A==1'b1)
state_next <= S0;
else
state_next <= S1;
S1: if(A==1'b1)
state_next <= S2;
else
state_next <= S1;
S2: if(A==1'b1)
state_next <= S3;
else
state_next <= S1;
S3: if(A==1'b1)
state_next <= S4;
else
state_next <= S1;
S4: if(A==1'b1)
state_next <= S0;
else
state_next <= S5;
S5: if(A==1'b1)
state_next <= S2;
else
state_next <= S6;
S6: if(A==1'b1)
state_next <= S2;
else
state_next <= S7;
S7: if(A==1'b1)
state_next <= S8;
else
state_next <= S2;
S8: if(A==1'b1)
state_next <= S3;
else
state_next <= S1;
default:state_next <= S0;
endcase
end
always@(posedge Clk or negedge Rst_n)
begin
if(!Rst_n)
Flag <= 1'b0;
else if(state == S8)
Flag <= 1'b1;
else
Flag <= 1'b0;
end
endmodule