一、有限状态机
- 基本概念
有限状态机(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.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
三、状态编码
常用编码方式:文章来源:https://www.toymoban.com/news/detail-579205.html
- 顺序编码 采用顺序的二进制数编码每个状态,例有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.文章来源地址https://www.toymoban.com/news/detail-579205.html
到了这里,关于有限状态机设计(Verilog HDL)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!