有限状态机设计(Verilog HDL)

一、有限状态机

- 基本概念
有限状态机(Finite State Machine, FSM)是电路设计的经典方法,通常可以认为是组合逻辑和寄存器逻辑的组合,其中组合逻辑用于状态译码和产生输出信号,寄存器用于存储状态。

- Moore和Mealy型状态机
摩尔型(Moore)状态机: 输出只是当前状态的函数
米利型(Mealy)状态机: 输出是当前状态和当前输入的函数

似乎不太好理解,我们结合状态机模型来看一下

![在这里插入图片描述](https://img-blog.csdnimg.cn/c703df85f31c4ff1995701978f8be5f2.png
可以看出,Moore型状态机输出只与当前状态(现态CS)有关

在这里插入图片描述
图中可以看出,Mealy型状态机相较于Moore型状态机,其输出逻辑多了一个输入端,即上述定义所说的Mealy型状态机输出由当前状态(现态CS)和当前输入决定。

- 两种状态机的区别
由于两者模型的差别,不难看出,Mealy型状态机当输入改变时输出也会立即改变,不依赖时钟,而Moore型状态机输入状态改变时,需要经过时钟同步后输出才会改变,即Moore型状态机比Mealy型状态机输出要多一个时钟周期。实用的状态机一般都设计成同步时序模式。

二、有限状态机的Verilog描述

-状态机的设计中主要包含以下3个对象:

  • 当前状态,或称为现态(Current State, CS)
  • 下一状态,或称为次态(Next State, NS)
  • 输出逻辑(Out Logic, OL)

-Verilog描述方式

  • 三段式描述(CS、NS、OL)
  • 两段式描述(CS+NS、OL)或(CS、NS+OL)
  • 单段式描述(CS+NS+OL)

-举例(可乐机:2.5¥出可乐,可以投入0.5¥、1¥)
1.先画出状态转移图,比较丑,但大概是这样,简单起见,不考虑找零,不投硬币也不算输入。
在这里插入图片描述
2.Verilog描述

2.1三段式描述

//三段式描述(CS、NS、OL)

module  Coke_Machine										//模块名
(
	input		wire		sys_clk		 	,				//输入时钟
	input		wire		sys_rst_n	 	,				//输入复位
	input		wire		pi_money_half	,				//输入0.5
	input		wire		pi_money_one	,				//输入1.0
	
	output	reg		po_cola
);

parameter		IDLE      = 5'b00001,			//状态编码,独热码
				HALF	  = 5'b00010,
				ONE       = 5'b00100,
				ONE_HALF  = 5'b01000,
				TWO	      = 5'b10000;

wire	[1:0]		pi_money;
reg 	[4:0]		state,next_state;

assign	pi_money = {pi_money_one,pi_money_half};				//合并输入,即输入0.5为01,输入1为10

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		state <= IDLE;											//定义起始状态
	else
		state <= next_state;

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		state <= IDLE;											//定义起始状态
	else	case(state)
		IDLE:		if(pi_money == 2'b01)
						next_state <= HALF;
					else if(pi_money == 2'b10)
						next_state <= ONE;
					else
						next_state <= IDLE;
		HALF:		if(pi_money == 2'b01)
						next_state <= ONE;
					else if(pi_money == 2'b10)
						next_state <= ONE_HALF;
					else
						next_state <= HALF;
		ONE:		if(pi_money == 2'b01)
						next_state <= ONE_HALF;
					else if(pi_money == 2'b10)
						next_state <= TWO;
					else
						next_state <= ONE;
		ONE_HALF:	if(pi_money == 2'b01)
						next_state <= TWO;
					else if(pi_money == 2'b10)
						next_state <= IDLE;
					else
						next_state <= ONE_HALF;
		TWO:		if(pi_money == 2'b01)
						next_state <= IDLE;
					else if(pi_money == 2'b10)
						next_state <= HALF;
					else
						next_state <= TWO;
		default:	next_state <= IDLE;
		endcase

always@(posedge sys_clk or negedge sys_rst_n)									//输出逻辑
	if(sys_rst_n == 1'b0)
		po_cola <= 1'b0;
	else if(((state == ONE_HALF) && (pi_money == 2'b10))
			|| ((state == TWO) && (pi_money == 2'b01))
			|| ((state == TWO) && (pi_money == 2'b10)))
		po_cola <= 1'b1;
	else
		po_cola <= 1'b0;
		
endmodule

2.2两段式描述(CS+NS、OL)

//两段式描述(CS+NS、OL)


module  Coke_Machine										//模块名
(
	input		wire		sys_clk		 	,				//输入时钟
	input		wire		sys_rst_n	 	,				//输入复位
	input		wire		pi_money_half	,				//输入01
	input		wire		pi_money_one	,				//输入10
	
	output	reg		po_cola
);

parameter		IDLE      = 5'b00001,			//状态编码,独热码
				HALF	  = 5'b00010,
				ONE       = 5'b00100,
				ONE_HALF  = 5'b01000,
				TWO	      = 5'b10000;

wire	[1:0]		pi_money;
reg 	[4:0]		state;

assign	pi_money = {pi_money_one,pi_money_half};				//合并输入,即输入0.5为01,输入1为10

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		state <= IDLE;											//定义起始状态
	else	case(state)
		IDLE:		if(pi_money == 2'b01)
						state <= HALF;
					else if(pi_money == 2'b10)
						state <= ONE;
					else
						state <= IDLE;
		HALF:		if(pi_money == 2'b01)
						state <= ONE;
					else if(pi_money == 2'b10)
						state <= ONE_HALF;
					else
						state <= HALF;
		ONE:		if(pi_money == 2'b01)
						state <= ONE_HALF;
					else if(pi_money == 2'b10)
						state <= TWO;
					else
						state <= ONE;
		ONE_HALF:	if(pi_money == 2'b01)
						state <= TWO;
					else if(pi_money == 2'b10)
						state <= IDLE;
					else
						state <= ONE_HALF;
		TWO:		if(pi_money == 2'b01)
						state <= IDLE;
					else if(pi_money == 2'b10)
						state <= HALF;
					else
						state <= TWO;
		default:	state <= IDLE;
		endcase

always@(posedge sys_clk or negedge sys_rst_n)									//输出逻辑
	if(sys_rst_n == 1'b0)
		po_cola <= 1'b0;
	else if(((state == ONE_HALF) && (pi_money == 2'b10))
			|| ((state == TWO) && (pi_money == 2'b01))
			|| ((state == TWO) && (pi_money == 2'b10)))
		po_cola <= 1'b1;
	else
		po_cola <= 1'b0;
		
endmodule

2.3单过程描述

//单段式描述


module  Coke_Machine										//模块名
(
	input		wire		sys_clk		 	,				//输入时钟
	input		wire		sys_rst_n	 	,				//输入复位
	input		wire		pi_money_half	,				//输入01
	input		wire		pi_money_one	,				//输入10
	
	output	reg		po_cola
);

parameter		IDLE      = 5'b00001,			//状态编码,独热码
				HALF	  = 5'b00010,
				ONE       = 5'b00100,
				ONE_HALF  = 5'b01000,
				TWO	      = 5'b10000;

wire	[1:0]		pi_money;
reg 	[4:0]		state;

assign	pi_money = {pi_money_one,pi_money_half};				//合并输入,即输入0.5为01,输入1为10

always@(posedge sys_clk or negedge sys_rst_n)
	if(sys_rst_n == 1'b0)
		state <= IDLE;											//定义起始状态
	else	case(state)
		IDLE:		if(pi_money == 2'b01)
					begin
						state <= HALF;
						po_cola <= 1'b0;
					end
					else if(pi_money == 2'b10)
					begin
						state <= ONE;
						po_cola <= 1'b0;
					end
					else
					begin
						state <= IDLE;
						po_cola <= 1'b0;
					end
		HALF:		if(pi_money == 2'b01)
					begin
						state <= ONE;
						po_cola <= 1'b0;
					end
					else if(pi_money == 2'b10)
					begin
						state <= ONE_HALF;
						po_cola <= 1'b0;
					end
					else
					begin
						state <= HALF;
						po_cola <= 1'b0;
					end
		ONE:		if(pi_money == 2'b01)
					begin
						state <= ONE_HALF;
						po_cola <= 1'b0;
					end
					else if(pi_money == 2'b10)
					begin
						state <= TWO;
						po_cola <= 1'b0;
					end
					else
					begin
						state <= ONE;
						po_cola <= 1'b0;
					end
		ONE_HALF:	if(pi_money == 2'b01)
					begin
						state <= TWO;
						po_cola <= 1'b0;
					end
					else if(pi_money == 2'b10)
					begin
						state <= IDLE;
						po_cola <= 1'b1;
					end
					else
					begin
						state <= ONE_HALF;
						po_cola <= 1'b0;
					end
		TWO:		if(pi_money == 2'b01)
					begin
						state <= IDLE;
						po_cola <= 1'b1;
					end
					else if(pi_money == 2'b10)
					begin
						state <= HALF;
						po_cola <= 1'b1;
					end
					else
					begin
						state <= TWO;
						po_cola <= 1'b0;
					end
		default:	
					begin
						state <= IDLE;
						po_cola <= 1'b0;
					end
		endcase
		
endmodule

三、状态编码

常用编码方式:

  1. 顺序编码 采用顺序的二进制数编码每个状态,例有4个状态:00、01、10、11。优点是占用位数少,缺点是从一个状态转换到相邻状态时,可能有多个比特位同时变化,容易产生毛刺,引发逻辑错误。
  2. 格雷编码 采用格雷码的方式编码每个状态,例有4个状态:00、01、11、10.优点是位数少节约了逻辑单元,因为相邻状态跳转只有一个比特位的改变,也减少了瞬变次数和毛刺产生的可能。
  3. 约翰逊编码 在约翰逊计数器的基础上引出约翰逊码,把输出最高位取反再反馈到最低位,例有6个状态:000、001、011、111、110、100,同样相邻两个状态只有一个比特位不同,但占用位宽多了。
  4. 1位热码编码(独热码) 采用n个触发器编码n个状态,例有4个状态:0001、0010、0100、1000 。独热码占用位数最多,但可以有效节省和简化译码电路,用的也较多。

**参考资料:
王金明.《FPGA设计与Verilog HDL实现》.北京:电子工业出版社,2021.

猜你喜欢

转载自blog.csdn.net/m0_51294753/article/details/128121323
HDL