有限状态机(Finite-State Machine,FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。状态机不仅是一种电路的描述工具,而且也是一种思想方法,在电路设计的系统级和 RTL 级有着广泛的应用。
Verilog 中状态机主要用于同步时序逻辑的设计,能够在有限个状态之间按一定要求和规律切换时序电路的状态。状态的切换方向不但取决于各个输入值,还取决于当前所在状态。 状态机可分为 2 类:Moore
状态机和 Mealy
状态机。
Moore
型状态机的输出只与当前状态有关,与当前输入无关。
输出会在一个完整的时钟周期内保持稳定,即使此时输入信号有变化,输出也不会变化。输入对输出的影响要到下一个时钟周期才能反映出来。这也是 Moore
型状态机的一个重要特点:输入与输出是隔离开来的。
Mealy
型状态机的输出,不仅与当前状态有关,还取决于当前的输入信号。
Mealy
型状态机的输出是在输入信号变化以后立刻发生变化,且输入变化可能出现在任何状态的时钟周期内。因此,同种逻辑下,Mealy
型状态机输出对输入的响应会比 Moore
型状态机早一个时钟周期。
根据设计需求画出状态转移图,确定使用状态机类型,并标注出各种输入输出信号,更有助于编程。一般使用最多的是 Mealy
型 3 段式状态机,下面用通过设计一个自动售卖机的具体实例来说明状态机的设计过程。
自动售卖机的功能描述如下:
饮料单价 2 元,该售卖机只能接受 0.5 元、1 元的硬币。考虑找零和出货。投币和出货过程都是一次一次的进行,不会出现一次性投入多币或一次性出货多瓶饮料的现象。每一轮售卖机接受投币、出货、找零完成后,才能进入到新的自动售卖状态。
该售卖机的工作状态转移图如下所示,包含了输入、输出信号状态。
其中,coin = 1 代表投入了 0.5 元硬币,coin = 2 代表投入了 1 元硬币。
状态机设计如下:
Mealy
型状态机,根据当前状态和当前输入,确定输出信号。// vending-machine
// 2 yuan for a bottle of drink
// only 2 coins supported: 5 jiao and 1 yuan
// finish the function of selling and changing
module vending_machine_p3 (
input clk ,
input rstn ,
input [1:0] coin , //01 for 0.5 jiao, 10 for 1 yuan
output [1:0] change ,
output sell //output the drink
);
//machine state decode
parameter IDLE = 3"d0 ;
parameter GET05 = 3"d1 ;
parameter GET10 = 3"d2 ;
parameter GET15 = 3"d3 ;
//machine variable
reg [2:0] st_next ;
reg [2:0] st_cur ;
//(1) state transfer
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
st_cur <= "b0 ;
end
else begin
st_cur <= st_next ;
end
end
//(2) state switch, using block assignment for combination-logic
//all case items need to be displayed completely
always @(*) begin
//st_next = st_cur ;//如果条件选项考虑不全,可以赋初值消除latch
case(st_cur)
IDLE:
case (coin)
2"b01: st_next = GET05 ;
2"b10: st_next = GET10 ;
default: st_next = IDLE ;
endcase
GET05:
case (coin)
2"b01: st_next = GET10 ;
2"b10: st_next = GET15 ;
default: st_next = GET05 ;
endcase
GET10:
case (coin)
2"b01: st_next = GET15 ;
2"b10: st_next = IDLE ;
default: st_next = GET10 ;
endcase
GET15:
case (coin)
2"b01,2"b10:
st_next = IDLE ;
default: st_next = GET15 ;
endcase
default: st_next = IDLE ;
endcase
end
//(3) output logic, using non-block assignment
reg [1:0] change_r ;
reg sell_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
change_r <= 2"b0 ;
sell_r <= 1"b0 ;
end
else if ((st_cur == GET15 && coin ==2"h1)
|| (st_cur == GET10 && coin ==2"d2)) begin
change_r <= 2"b0 ;
sell_r <= 1"b1 ;
end
else if (st_cur == GET15 && coin == 2"h2) begin
change_r <= 2"b1 ;
sell_r <= 1"b1 ;
end
else begin
change_r <= 2"b0 ;
sell_r <= 1"b0 ;
end
end
assign sell = sell_r ;
assign change = change_r ;
endmodule
testbench 设计如下。仿真中模拟了 4 种情景,分别是:
case1 对应连续输入 4 个 5 角硬币;case2 对应 1 元 - 5 角 - 1 元的投币顺序;case3 对应 5 角 - 1 元 - 5 角的投币顺序;case4 对应连续 3 个 5 角然后一个 1 元的投币顺序。
`timescale 1ns/1ps
module test ;
reg clk;
reg rstn ;
reg [1:0] coin ;
wire [1:0] change ;
wire sell ;
//clock generating
parameter CYCLE_200MHz = 10 ; //
always begin
clk = 0 ; #(CYCLE_200MHz/2) ;
clk = 1 ; #(CYCLE_200MHz/2) ;
end
//motivation generating
reg [9:0] buy_oper ; //store state of the buy operation
initial begin
buy_oper = "h0 ;
coin = 2"h0 ;
rstn = 1"b0 ;
#8 rstn = 1"b1 ;
@(negedge clk) ;
//case(1) 0.5 -> 0.5 -> 0.5 -> 0.5
#16 ;
buy_oper = 10"b00_0101_0101 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
//case(2) 1 -> 0.5 -> 1, taking change
#16 ;
buy_oper = 10"b00_0010_0110 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
//case(3) 0.5 -> 1 -> 0.5
#16 ;
buy_oper = 10"b00_0001_1001 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
//case(4) 0.5 -> 0.5 -> 0.5 -> 1, taking change
#16 ;
buy_oper = 10"b00_1001_0101 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
end
//(1) mealy state with 3-stage
vending_machine_p3 u_mealy_p3 (
.clk (clk),
.rstn (rstn),
.coin (coin),
.change (change),
.sell (sell)
);
//simulation finish
always begin
#100;
if ($time >= 10000) $finish ;
end
endmodule // test
仿真结果如下:
由图可知,代表出货动作的信号 sell 都能在投币完毕后正常的拉高,而代表找零动作的信号 change 也都能根据输入的硬币场景输出正确的是否找零信号。
将 3 段式状态机 2、3 段描述合并,其他部分保持不变,状态机就变成了 2 段式描述。
修改部分如下:
//(2) state switch, and output logic
//all using block assignment for combination-logic
reg [1:0] change_r ;
reg sell_r ;
always @(*) begin //all case items need to be displayed completely
case(st_cur)
IDLE: begin
change_r = 2"b0 ;
sell_r = 1"b0 ;
case (coin)
2"b01: st_next = GET05 ;
2"b10: st_next = GET10 ;
default: st_next = IDLE ;
endcase // case (coin)
end
GET05: begin
change_r = 2"b0 ;
sell_r = 1"b0 ;
case (coin)
2"b01: st_next = GET10 ;
2"b10: st_next = GET15 ;
default: st_next = GET05 ;
endcase // case (coin)
end
GET10:
case (coin)
2"b01: begin
st_next = GET15 ;
change_r = 2"b0 ;
sell_r = 1"b0 ;
end
2"b10: begin
st_next = IDLE ;
change_r = 2"b0 ;
sell_r = 1"b1 ;
end
default: begin
st_next = GET10 ;
change_r = 2"b0 ;
sell_r = 1"b0 ;
end
endcase // case (coin)
GET15:
case (coin)
2"b01: begin
st_next = IDLE ;
change_r = 2"b0 ;
sell_r = 1"b1 ;
end
2"b10: begin
st_next = IDLE ;
change_r = 2"b1 ;
sell_r = 1"b1 ;
end
default: begin
st_next = GET15 ;
change_r = 2"b0 ;
sell_r = 1"b0 ;
end
endcase
default: begin
st_next = IDLE ;
change_r = 2"b0 ;
sell_r = 1"b0 ;
end
endcase
end
将上述修改的新模块例化到 3 段式的 testbench 中即可进行仿真,结果如下:
由图可知,出货信号 sell 和 找零信号 change 相对于 3 段式状态机输出提前了一个时钟周期,这是因为输出信号都是阻塞赋值导致的。
如图中红色圆圈部分,输出信号都出现了干扰脉冲,这是因为输入信号都是异步的,而且输出信号是组合逻辑输出,没有时钟驱动。
实际中,如果输入信号都是与时钟同步的,这种干扰脉冲是不会出现的。如果是异步输入信号,首先应当对信号进行同步。
将 3 段式状态机 1、 2、3 段描述合并,状态机就变成了 1 段式描述。
修改部分如下:
//(1) using one state-variable do describe
reg [1:0] change_r ;
reg sell_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
st_cur <= "b0 ;
change_r <= 2"b0 ;
sell_r <= 1"b0 ;
end
else begin
case(st_cur)
IDLE: begin
change_r <= 2"b0 ;
sell_r <= 1"b0 ;
case (coin)
2"b01: st_cur <= GET05 ;
2"b10: st_cur <= GET10 ;
endcase
end
GET05: begin
case (coin)
2"b01: st_cur <= GET10 ;
2"b10: st_cur <= GET15 ;
endcase
end
GET10:
case (coin)
2"b01: st_cur <= GET15 ;
2"b10: begin
st_cur <= IDLE ;
sell_r <= 1"b1 ;
end
endcase
GET15:
case (coin)
2"b01: begin
st_cur <= IDLE ;
sell_r <= 1"b1 ;
end
2"b10: begin
st_cur <= IDLE ;
change_r <= 2"b1 ;
sell_r <= 1"b1 ;
end
endcase
default: begin
st_cur <= IDLE ;
end
endcase // case (st_cur)
end // else: !if(!rstn)
end
将上述修改的新模块例化到 3 段式的 testbench 中即可进行仿真,结果如下:
由图可知,输出信号与 3 段式状态机完全一致。
1 段式状态机的缺点就是许多种逻辑糅合在一起,不易后期的维护。当状态机和输出信号较少时,可以尝试此种描述方式。
如果使用 Moore
型状态机描述售卖机的工作流程,那么还需要再增加 2 个状态编码,用以描述 Mealy
状态机输出时的输入信号和状态机状态。
3 段式 Moore
型状态机描述的自动售卖机 Verilog 代码如下:
module vending_machine_moore (
input clk ,
input rstn ,
input [1:0] coin , //01 for 0.5 jiao, 10 for 1 yuan
output [1:0] change ,
output sell //output the drink
);
//machine state decode
parameter IDLE = 3"d0 ;
parameter GET05 = 3"d1 ;
parameter GET10 = 3"d2 ;
parameter GET15 = 3"d3 ;
// new state for moore state-machine
parameter GET20 = 3"d4 ;
parameter GET25 = 3"d5 ;
//machine variable
reg [2:0] st_next ;
reg [2:0] st_cur ;
//(1) state transfer
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
st_cur <= "b0 ;
end
else begin
st_cur <= st_next ;
end
end
//(2) state switch, using block assignment for combination-logic
always @(*) begin //all case items need to be displayed completely
case(st_cur)
IDLE:
case (coin)
2"b01: st_next = GET05 ;
2"b10: st_next = GET10 ;
default: st_next = IDLE ;
endcase
GET05:
case (coin)
2"b01: st_next = GET10 ;
2"b10: st_next = GET15 ;
default: st_next = GET05 ;
endcase
GET10:
case (coin)
2"b01: st_next = GET15 ;
2"b10: st_next = GET20 ;
default: st_next = GET10 ;
endcase
GET15:
case (coin)
2"b01: st_next = GET20 ;
2"b10: st_next = GET25 ;
default: st_next = GET15 ;
endcase
GET20: st_next = IDLE ;
GET25: st_next = IDLE ;
default: st_next = IDLE ;
endcase // case (st_cur)
end // always @ (*)
// (3) output logic,
// one cycle delayed when using non-block assignment
reg [1:0] change_r ;
reg sell_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
change_r <= 2"b0 ;
sell_r <= 1"b0 ;
end
else if (st_cur == GET20 ) begin
sell_r <= 1"b1 ;
end
else if (st_cur == GET25) begin
change_r <= 2"b1 ;
sell_r <= 1"b1 ;
end
else begin
change_r <= 2"b0 ;
sell_r <= 1"b0 ;
end
end
assign sell = sell_r ;
assign change = change_r ;
endmodule
将上述修改的 Moore
状态机例化到 3 段式的 testbench 中即可进行仿真,结果如下:
由图可知,输出信号与 Mealy
型 3 段式状态机相比延迟了一个时钟周期,这是因为进入到新增加的编码状态机时需要一个时钟周期的时延。此时,输出再用非阻塞赋值就会导致最终的输出信号延迟一个时钟周期。这也属于 Moore
型状态机的特点。
输出信号赋值时,用阻塞赋值,则可以提前一个时钟周期。
输出逻辑修改如下:
// (3.2) output logic, using block assignment
reg [1:0] change_r ;
reg sell_r ;
always @(*) begin
change_r = "b0 ;
sell_r = "b0 ; //not list all condition, initializing them
if (st_cur == GET20 ) begin
sell_r = 1"b1 ;
end
else if (st_cur == GET25) begin
change_r = 2"b1 ;
sell_r = 1"b1 ;
end
end
输出信号阻塞赋值的仿真结果如下:
由图可知,输出信号已经和 3 段式 Mealy
型状态机一致。
点击这里下载源码
在本节中,我们将学习如何使用不同的传感器连接我们的Arduino板。我们将讨论以下传感器: 湿度传感器(DHT22)温度传感器(LM35...
desktopCapturer模块可用来获取可用资源,这个资源可通过getUserMedia捕获得到.// 在渲染进程中.var desktopCapturer = require(...
设备模式移动仿真随着移动用户的增长,移动端友好的响应式网站设计变得越来越重要。网页的内容要在不同的设备以及各种网络环境下...
凹凸图用于使用Measure值之一来比较两个尺寸。它们对于探索时间维度或地方维度或与分析相关的其他维度的值的变化非常有用。凹凸...
项目脚手架grunt-initgrunt-init是一个用于自动创建项目脚手架的工具。它会基于当前工作环境和你给出的一些配置选项构建一个完整...
grunt.optionGrunt的option API被用来在多个任务之间共享参数、访问命令行中设置的参数。一个简单的案例就是为一个目标(target...
介绍WipeLeft类定义了一个擦除左效果。 组件的前或后状态必须是不可见的。类声明以下是 mx.effects.WipeLeft 类的声明:public cl...
IPv4/IPv6双协议栈FEATURESTATE:Kubernetesv1.23[stable]IPv4/IPv6双协议栈网络能够将IPv4和IPv6地址分配给Pod和Service。从1.21...
限制范围默认情况下,Kubernetes集群上的容器运行使用的计算资源没有限制。使用资源配额,集群管理员可以以名字空间为单位,限制...
污点和容忍度节点亲和性是Pod的一种属性,它使Pod被吸引到一类特定的节点(这可能出于一种偏好,也可能是硬性要求)。污点(Tain...