状态机设计(Verilog HDL)"/>
有限状态机设计(Verilog HDL)
一、有限状态机
- 基本概念
有限状态机(Finite State Machine, FSM)是电路设计的经典方法,通常可以认为是组合逻辑和寄存器逻辑的组合,其中组合逻辑用于状态译码和产生输出信号,寄存器用于存储状态。
- Moore和Mealy型状态机
摩尔型(Moore)状态机: 输出只是当前状态的函数
米利型(Mealy)状态机: 输出是当前状态和当前输入的函数
似乎不太好理解,我们结合状态机模型来看一下
可以看出,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.5input wire pi_money_one , //输入1.0output 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为10always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n == 1'b0)state <= IDLE; //定义起始状态elsestate <= 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;elsenext_state <= IDLE;HALF: if(pi_money == 2'b01)next_state <= ONE;else if(pi_money == 2'b10)next_state <= ONE_HALF;elsenext_state <= HALF;ONE: if(pi_money == 2'b01)next_state <= ONE_HALF;else if(pi_money == 2'b10)next_state <= TWO;elsenext_state <= ONE;ONE_HALF: if(pi_money == 2'b01)next_state <= TWO;else if(pi_money == 2'b10)next_state <= IDLE;elsenext_state <= ONE_HALF;TWO: if(pi_money == 2'b01)next_state <= IDLE;else if(pi_money == 2'b10)next_state <= HALF;elsenext_state <= TWO;default: next_state <= IDLE;endcasealways@(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;elsepo_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 , //输入01input wire pi_money_one , //输入10output 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为10always@(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;elsestate <= IDLE;HALF: if(pi_money == 2'b01)state <= ONE;else if(pi_money == 2'b10)state <= ONE_HALF;elsestate <= HALF;ONE: if(pi_money == 2'b01)state <= ONE_HALF;else if(pi_money == 2'b10)state <= TWO;elsestate <= ONE;ONE_HALF: if(pi_money == 2'b01)state <= TWO;else if(pi_money == 2'b10)state <= IDLE;elsestate <= ONE_HALF;TWO: if(pi_money == 2'b01)state <= IDLE;else if(pi_money == 2'b10)state <= HALF;elsestate <= TWO;default: state <= IDLE;endcasealways@(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;elsepo_cola <= 1'b0;endmodule
2.3单过程描述
//单段式描述module Coke_Machine //模块名
(input wire sys_clk , //输入时钟input wire sys_rst_n , //输入复位input wire pi_money_half , //输入01input wire pi_money_one , //输入10output 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为10always@(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)beginstate <= HALF;po_cola <= 1'b0;endelse if(pi_money == 2'b10)beginstate <= ONE;po_cola <= 1'b0;endelsebeginstate <= IDLE;po_cola <= 1'b0;endHALF: if(pi_money == 2'b01)beginstate <= ONE;po_cola <= 1'b0;endelse if(pi_money == 2'b10)beginstate <= ONE_HALF;po_cola <= 1'b0;endelsebeginstate <= HALF;po_cola <= 1'b0;endONE: if(pi_money == 2'b01)beginstate <= ONE_HALF;po_cola <= 1'b0;endelse if(pi_money == 2'b10)beginstate <= TWO;po_cola <= 1'b0;endelsebeginstate <= ONE;po_cola <= 1'b0;endONE_HALF: if(pi_money == 2'b01)beginstate <= TWO;po_cola <= 1'b0;endelse if(pi_money == 2'b10)beginstate <= IDLE;po_cola <= 1'b1;endelsebeginstate <= ONE_HALF;po_cola <= 1'b0;endTWO: if(pi_money == 2'b01)beginstate <= IDLE;po_cola <= 1'b1;endelse if(pi_money == 2'b10)beginstate <= HALF;po_cola <= 1'b1;endelsebeginstate <= TWO;po_cola <= 1'b0;enddefault: beginstate <= IDLE;po_cola <= 1'b0;endendcaseendmodule
三、状态编码
常用编码方式:
- 顺序编码 采用顺序的二进制数编码每个状态,例有4个状态:00、01、10、11。优点是占用位数少,缺点是从一个状态转换到相邻状态时,可能有多个比特位同时变化,容易产生毛刺,引发逻辑错误。
- 格雷编码 采用格雷码的方式编码每个状态,例有4个状态:00、01、11、10.优点是位数少节约了逻辑单元,因为相邻状态跳转只有一个比特位的改变,也减少了瞬变次数和毛刺产生的可能。
- 约翰逊编码 在约翰逊计数器的基础上引出约翰逊码,把输出最高位取反再反馈到最低位,例有6个状态:000、001、011、111、110、100,同样相邻两个状态只有一个比特位不同,但占用位宽多了。
- 1位热码编码(独热码) 采用n个触发器编码n个状态,例有4个状态:0001、0010、0100、1000 。独热码占用位数最多,但可以有效节省和简化译码电路,用的也较多。
**参考资料:
王金明.《FPGA设计与Verilog HDL实现》.北京:电子工业出版社,2021.
更多推荐
有限状态机设计(Verilog HDL)
发布评论