赞
踩
一、状态机简介
FPGA中的状态机是一种基于状态转移的设计方法,它可以实现复杂的控制逻辑和状态管理。状态机通常由状态寄存器、状态转移逻辑和输出逻辑组成。
状态寄存器用于存储当前状态,状态转移逻辑用于根据输入信号和当前状态计算下一个状态,输出逻辑用于根据当前状态和输入信号计算输出信号。状态机可以实现多种功能,例如序列检测、计数器、状态控制等。
在FPGA中,状态机可以使用Verilog或VHDL等硬件描述语言进行设计和实现。我们需要定义状态寄存器、状态转移逻辑和输出逻辑,并将其综合到FPGA中。状态机的设计需要考虑时序约束、时钟域等因素,以确保正确性和可靠性。
二、分类
状态机可以根据不同的特征进行分类,常见的分类方式包括以下几种:
Moore状态机和Mealy状态机:Moore状态机的输出只与当前状态有关,而Mealy状态机的输出不仅与当前状态有关,还与输入信号有关。
同步状态机和异步状态机:同步状态机的状态转移和输出逻辑都与时钟信号同步,而异步状态机的状态转移和输出逻辑不与时钟信号同步。
有限状态自动机和无限状态自动机:主要是指状态数是否是有限的。
顺序状态机和组合状态机:顺序状态机的输出不仅与当前输入有关,还与之前的输入有关,而组合状态机的输出只与当前输入有关。
硬件状态机和软件状态机:硬件状态机是在FPGA中实现的,而软件状态机是在软件中实现的。硬件状态机具有高效、低功耗、可重构等优点,而软件状态机具有灵活、易于修改等优点。
三、售货机实验
主要实现功能:除时钟和复位外,定义两种输入——0.5元和1元硬币;售卖机有5种状态——空闲、已投入0.5元到2元4种状态;定义两种输出——找零0.5元和出可乐。
四、代码
选用两段式,一段为时序逻辑描述转移,一段为组合逻辑描述输出。在进行综合时,系统会自动将这种代码识别为状态机。
- `timescale 1ns / 1ps //设置时间单位
-
-
- module cola_fsm2(
- input wire clk,
- input wire rst_n,
- input wire pi_money_one,
- input wire pi_money_half,
- output wire po_cola,
- output wire po_money
- );
- //定义参数类型
- reg [2:0] state;
- reg po_cola_p, po_money_p;
- wire [1:0] pi_money;
-
- //设置状态参数
- parameter
- idle=3'b000,half=3'b001,one=3'b010,onehalf=3'b011,
- two=3'b100;
- //使用拼接符赋值
- assign pi_money = {pi_money_one,pi_money_half};
- //时序逻辑描述转移
- always @(posedge clk or negedge rst_n)
- if(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 <= onehalf;
- else
- state <= half;
- one : if(pi_money == 2'b01)
- state <= onehalf;
- else if(pi_money== 2'b10)
- state <= two;
- else
- state <= one;
- onehalf:if(pi_money == 2'b01)
- state <= two;
- else if(pi_money == 2'b10)
- state <= idle;
- else
- state <= onehalf;
- two : if(pi_money == 2'b01)
- state <= idle;
- else if(pi_money== 2'b10)
- state <= idle;
- else
- state <= two;
- default: state <= idle;
- endcase
-
- //组合逻辑描述输出
- always @ (posedge clk or negedge rst_n)
- if (rst_n == 1'b0)
- state <= idle;
- else if (((state == two) && (pi_money == 2'b01)) || ((state==onehalf) && (pi_money==2'b10)))
- //使用并行块非阻塞赋值
- fork
- po_cola_p <= 1'b1;
- po_money_p <= 1'b0;
- join
- else if ((state == two )&& (pi_money == 2'b10))
- fork
- po_cola_p <= 1'b1;
- po_money_p <= 1'b1;
- join
- else
- fork
- po_cola_p <= 1'b0;
- po_money_p <= 1'b0;
- join
-
- //将寄存器值分配给输出
- assign po_cola=po_cola_p;
- assign po_money=po_money_p;
-
- endmodule
五、编写仿真文件
- `timescale 1ns / 1ps
-
- module cola_fsm2_tb(
- );
- reg clk,rst_n,pi_money_one,pi_money_half;
- reg random;
- wire po_money,po_cola;
-
- always #20 clk = ~clk; //产生一个以40ns为周期的时钟
-
- //在前半个时钟使复位键处于高电平
- initial
- begin
- rst_n <= 1'b0;
- clk <= 1'b1;
- #20
- rst_n <= 1'b1;
- end
- //将投币面值设置为随机数,
- always @(posedge clk or negedge rst_n)
- if(rst_n == 1'b0)
- random=0;
- else
- random <= {$random}%2;
-
- always @(posedge clk or negedge rst_n)
- if(rst_n == 1'b0)
- fork
- pi_money_half <= 1'b0;
- pi_money_one <= 1'b0;
- join
- else
- fork
- pi_money_one <= random;
- pi_money_half <= ~random;
- join
- //将状态和输入硬币与引用模块的成员连接
- wire [2:0] state=cola_fsm2_tb.state;
- wire [1:0] pi_money=cola_fsm2_tb.pi_money;
- initial
- begin
- $timeformat(-9,0,"ns",6);//设置仿真时间格式,-9表示时间单位为ns、精度为1ns
- //监视信号的值并在控制台上显示,监视的信号有变化时自动更新
- $monitor("time=%t,state=%b,random=%b,po_money=%b,po_cola=%b",$time,state,random,po_money,po_cola);
- end
- //引用源模块
- cola_fsm2 cola_fsm2_tb(
- .clk(clk),
- .rst_n(rst_n),
- .pi_money_one(pi_money_one),
- .pi_money_half(pi_money_half),
- .po_cola(po_cola),
- .po_money(po_money)
- );
-
- endmodule
六、仿真结果分析
观察仿真结果可知:在第2、3个时钟周期分别投入了0.5元,第4、5个时钟周期分别投入了1元,共计3元,此时state值为4(two状态),因为是非阻塞赋值,所以在下一个时钟周期才会吐出可乐和找零0.5元,同时state跳转为0(idle状态)。
声明:此实验为野火FPGA状态机教程之二,基本架构相同,代码有所修改,欢迎交流讨论。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。