Verilog学习笔记 ——同步有限状态机

Verilog学习笔记 ——同步有限状态机

有限状态机

有限状态机可以认为是组合逻辑和寄存器逻辑的特殊组合,他一般包括组合逻辑和寄存器逻辑两个部分,寄存器用于存储状态,组合逻辑用于状态译码和产生输出信号。
根据输出信号产生方法不同,状态机可分为两类:摩尔型(Moore)和米里型(Mealy)
米里型状态机的输出不但取决于状态还取决于输入,即下一个状态=F(当前状态,输入信号);输出信号=G(当前状态,输入信号),电路结构如下图所示。
Verilog学习笔记 ——同步有限状态机_第1张图片

摩尔型状态机的输出只取决于当前状态,且只在时钟边沿到来时才会有状态变化。即下一个状态=F(当前状态,输入信号);输出信号=G(当前状态),电路结构如下图所示。
Verilog学习笔记 ——同步有限状态机_第2张图片
  在设计高速电路时,常常有必要使状态机的输出与时钟几乎完全同步。有一个办法是把状态变量直接用作输出,这种设计思路,在高速状态机电路时常常使用,这称为输出编码的状态指定。这种状态机也属于摩尔状态机。另一个方法是在输出逻辑G后面再加一组与时钟同步的寄存器输出流水线寄存器,让G所有的输出信号在下一个时钟跳变沿时同时存入寄存器组,即完全同步输出,这种输出称为流水线化的输出。如下图所示:
Verilog学习笔记 ——同步有限状态机_第3张图片

  把状态机精确地分为哪几类,其实并不重要,重要的是设计者如何把握输出的结构能满足设计的整体目标,包括定时的准确性和灵活性。

几种不同状态机写法

在状态机设计中主要包含以下三个对象:

  1. 当前状态,或称为现态(Current State,CS);
  2. 下一个状态,或称为次态(Next State,NS);
  3. 输出逻辑(OUT Logic,OL)。

再用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”序列检测器的状态转化图如下所示:
Verilog学习笔记 ——同步有限状态机_第4张图片
“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

你可能感兴趣的:(FPGA,fpga开发,fpga)