前言
学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。
学习视频:是根据野火FPGA视频教程——第十九讲
https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3
理论学习
关于理论学习请参考《数字电子技术基础》相关笔记:
《数字电子技术基础》6.4 时序逻辑电路——设计方法(FSM)有限状态机_追逐者-桥的博客-CSDN博客时序逻辑电路设计——有限状态机(FSM)https://blog.csdn.net/ARM_qiao/article/details/124461906状态机(FSM),也称为同步有限状态机,Mealy型、Moore型状态机(输出与输入无关)
实战演练一
一、设计规划
1.1 实验目标
可乐机每次只能投入1枚1元硬币,且每瓶可乐卖3元钱,投入3个硬币就可以让可乐机出一瓶可乐,如果投币不够3元放弃投币需要按复位键,否则之前的投币不能退回。
二、程序设计
2.1 波形图绘制
X表示输入:1投入1元硬币,0不投入硬币
Y表示输出:1出可乐,0不出可乐
S0、S1、S2状态:投入0元(初始状态)、投入1元、投入2元、投入3元。
2.2 代码编写
状态编码定义方式:
在低速系统中
- 状态机个数 <4个,二进制码
- 状态机个数 4—24个,独热码
- 状态机个数 >4个,格雷码
在高速系统中,无论个数均推荐使用独热码。
module simple_fsm(
input wire sys_clk,
input wire sys_rst_n,
input wire pi_money,
output reg po_cola
);
//状态定义,独热码,其他二进制码、格雷码编码(只有一位发生变化,减少毛刺)
parameter IDLE = 3'b001,
ONE = 3'b010,
TWO = 3'b100;
reg [2:0] 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 == 1'b1)
state <= ONE;
else
state <= IDLE;
ONE: if(pi_money == 1'b1)
state <= TWO;
else
state <= ONE;
TWO: if(pi_money == 1'b1)
state <= IDLE;
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 == TWO) && (pi_money == 1'b1))
po_cola <= 1'b1;
else
po_cola <= 1'b0;
endmodule
三、逻辑仿真
3.1 仿真代码
`timescale 1ns / 1ns
//
// Company: 追逐者——桥的小作坊
// Create Date: 2022/05/16 15:42:33
// Design Name: 有限状态机
// Module Name: tb_simple_fsm
///
module tb_simple_fsm();
reg sys_clk;
reg sys_rst_n;
reg pi_money;
wire po_cola;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_money <= 1'b0;
else
pi_money <= {$random} % 2;
wire [2:0] state = simple_fsm_inst.state;
initial begin
$timeformat(-9, 0, "ns", 6);
$monitor("@time %t:pi_money=%b, state=%b, po_cola=%b",$time, pi_money, state, po_cola);
end
simple_fsm simple_fsm_inst(
.sys_clk (sys_clk ),
.sys_rst_n(sys_rst_n),
.pi_money (pi_money ),
.po_cola (po_cola )
);
endmodule
3.2 波形图对比
实战演练二
一、设计规划
1.1 实验目标
可乐机定价为2.5元一瓶,可投入0.5元、1元硬币,投币不够2.5元需要按复位键退回钱款,投币超过2.5元找零。
二、程序设计
2.1 波形图绘制
输入:不投硬币,投入0.5硬币,投入1元硬币
输出:不出可乐/不找零,出可乐/不找零,出可乐/找零
状态:投入0元(初始状态)、投入0.5元、投入1元、投入1.5元、投入2元、投入2.5元、投入3元。
抽象编码:
输入:00,01,10
输出:00,10,11
2.2 代码编写
module complex_fsm(
input wire sys_clk ,
input wire sys_rst_n,
input wire pi_money_half,
input wire pi_money_one,
output reg po_cola,
output reg po_money
);
parameter IDLE = 5'b00001,
HALF = 5'b00010,
ONE = 5'b00100,
ONE_H = 5'b01000,
TWO = 5'b10000;
wire [1:0] pi_money; //用assign连续赋值语句
reg [4:0] state;
assign pi_money = {pi_money_one, pi_money_half};
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_H;
else
state <= HALF;
ONE : if(pi_money == 2'b01)
state <= ONE_H;
else if(pi_money == 2'b10)
state <= TWO;
else
state <= ONE;
ONE_H: if(pi_money == 2'b01)
state <= TWO;
else if(pi_money == 2'b10)
state <= IDLE;
else
state <= ONE_H;
TWO : if((pi_money == 2'b01) || (pi_money == 2'b10))
state <= IDLE;
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((stat == ONE_H && pi_money == 2'b10)
||(stat == TWO && pi_money == 2'b01)
||(stat == TWO && pi_money == 2'b10))
po_cola <= 1'b1;
else
po_cola <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_money <= 1'b0;
else if((stat == TWO) && (pi_money == 2'b10))
po_money <= 1'b1;
else
po_money <= 1'b0;
endmodule
三、逻辑仿真
3.1 仿真代码
`timescale 1ns / 1ns
//
// Company: 追逐者——桥的小作坊
// Create Date: 2022/05/16 18:39:18
// Design Name: 复杂可乐鸡
// Module Name: tb_comple_fsm
///
module tb_comple_fsm();
reg sys_clk;
reg sys_rst_n;
reg pi_money_half;
reg pi_money_one;
wire po_cola;
wire po_money;
reg random_data_gen;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
//由于不能同时投0.5和1元,因此随机数需要统一
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
random_data_gen <= 1'b0;
else
random_data_gen <= {$random} % 2;
//模拟投币
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_money_half <= 1'b0;
else
pi_money_half <= random_data_gen;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_money_one <= 1'b0;
else if((({$random}%2)==1'b1) && (pi_money_half==1'b0))
pi_money_one <= 1'b1;
else
pi_money_one <= ~random_data_gen;
wire [4:0] state = complex_fsm_inst.state;
wire [1:0] pi_money = complex_fsm_inst.pi_money;
initial begin
$timeformat(-9, 0, "ns", 6);
$monitor("@time %t:pi_money_half=%b, pi_money_one =%b, state=%b, pi_money=%b, po_cola=%b, po_money=%b",$time,
pi_money_half, pi_money_one, state, pi_money, po_cola, po_money);
end
complex_fsm complex_fsm_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.pi_money_half(pi_money_half),
.pi_money_one (pi_money_one ),
.po_cola (po_cola ),
.po_money (po_money )
);
endmodule