概述
状态机
- 前言
- 1.知识准备
- 2.功能设计
- 3.仿真验证
- 4.总结
前言
状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作,完成特定操作的控制中心。
状态机分Moore(摩尔)状态机和Mealy(米利)型状态机。若输出只和状态有关而与输入无关,则称为Moore状态机。若输出不仅和状态有关而且和输入有关系,则称为Mealy状态机。
1.知识准备
1⃣️为什么要使用状态机?
先看一个例子:
“快递员正在送件,假定他在接下来的一个小时内需要按照已经定好的顺序给五个人送件,而这五个人分别住在不同的地方并且各自的快递种类和件数都不同。”
这个例子,因为包含较多的判断条件和不同输出条件,因而比较难用一般的语句进行较为直观的描述。此时,如果我们使用状态机来对上面的例子进行描述,那么代码将会更容易编写更加直观易读。
2⃣️如何描述一个状态机?
状态机具有4要素,即现态、条件、动作、次态。详解如下:①现态:是指当前所处的状态。
②条件:当该条件被满足时,将会触发一个动作或进行一次状态迁移。③动作:当条件满足时所执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。(动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。)④次态:条件满足后要迁往的新状态。次态是相对于现态而言的,次态一旦被激活,则会成为新的现态。
当我们设计状态机时,要依据四要素进行代码编写。
3⃣️状态数较多时应该如何处理?
状态机需要根据控制信号按照预先设定的状态进行状态转移,这就出现了如何对状态进行
有效编码的问题。其中最简单的就是直接使用二进制编码进行表示,除此之外还有格雷码、独热码。假设有八个状态从 A 到 R,利用不同的编码格式分别如表所示。
不同的编码方式具有不同优势,在本次设计中我们选择独热码。目前我们只需要知道,独热码使用更多的寄存器但是其组合逻辑相对简单,FPGA 中提供较多的时序逻辑较多所以使用独热码。
2.功能设计
**项目目标:**设计一个简易的可乐机,用户可以投币购买,当投入一元硬币数量达到3时,可乐机出可乐。投入一元硬币数量未达到3时,可乐机不输出。
根据我们的需求,画出波形图如下:
状态转移图:
根据我们画的图,编写代码如下:
module fsm
(
input sys_clk ,
input sys_rst_n ,
input pi_money , //投币信号
output reg po_cola //输出可乐信号
);
//使用独热码
parameter IDLE=3'b001;
parameter ONE =3'b010;
parameter TWO =3'b100;
reg [2:0]state; //使用了独热码,有三个状态则用三位宽的变量
/*状态跳转说明部分*/
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n) //复位时回到IDLE状态
state <= IDLE;
else if(pi_money)begin //投币信号到来时,根据此时的状态决定下一个状态
case(state)
IDLE : state <= ONE;
ONE : state <= TWO;
TWO : state <= IDLE;
default : state <= IDLE;
endcase
end
else
state <= state; //无复位或者投币信号时保持原有状态
end
/*状态输出说明部分*/
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
po_cola <= 1'b0;
else if((state == TWO) && (pi_money)) //处于TWO状态且再次收到投币时输出可乐
po_cola <= 1'b1;
else
po_cola <= 1'b0;
end
endmodule
在Quartus II中进行一次全编译后,查看RTL视图:
查看状态转移图:
3.仿真验证
为了验证我们的设计,需要编写testbench文件进行仿真验证
我们编写testbench文件,是为了产生满足条件的激励信号,同时对模块的输出进行捕捉,测试输出是否满足要求。
编写代码如下:
`timescale 1ns/1ns
module tb_fsm();
reg sys_clk;
reg sys_rst_n;
reg pi_money;
wire po_cola;
initial begin
sys_clk = 1'b0; sys_rst_n = 1'b0; pi_money = 1'b0;
#40
sys_rst_n = 1'b1;
end
always#10 sys_clk <= ~sys_clk; //时钟周期为20ns
always#20 pi_money <= {$random} % 2; //每隔20个时间单位生成一次随机的投币信号
wire [2:0] state = fsm_inst.state; //将state变量引出,便于波形仿真时观察
fsm fsm_inst //连接端口
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.pi_money (pi_money),
.po_cola (po_cola)
);
endmodule
我们需要预先将state设置为二进制,得到的仿真波形如下:
对局部进行放大,可以看出:
当复位信号为高电平时,状态机开始工作。在时钟的上升沿到来时,如果投币信号(pi_money)为高电平,则发生状态转移。状态转移顺序为3’b001➡️3’b010➡️3’b100,即分别对应着IDLE、ONE、TWO三个状态的独热编码。
当状态机为TWO状态,此时如果有投币信号,则状态机跳转到IDLE状态且输出可乐(po_cola)。
从这里看出来我们的设计是正确的。
4.总结
本次我们设计了简单的状态机,实现了可乐机的功能。通过这个项目我们初步掌握了状态机代码的书写方式,为以后我们深入理解状态机打下基础。状态机的书写方式比较灵活,可以分为一段式、二段式 和三段式,具体三者的差距我们后续再进行学习。
最后
以上就是活泼白昼为你收集整理的fpga自学之路[2]状态机(附源码)前言的全部内容,希望文章能够帮你解决fpga自学之路[2]状态机(附源码)前言所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复