1.为什么要使用状态机?
Verilog语言都是并行执行的,但是很多情况下我们希望按照顺序的方式进行,而状态机就可以很好的实现顺序执行。
2.状态机分为mealy型和moore型
Mealy:输出状态不仅与当前状态有关,还与输入有关。
Moore:输出只与当前状态有关。
设计时不需要关注是什么类型。
3.状态机设计规则:使用两段式结构
方法:使用一个always块描述状态转移。使用另一个always块描述数据输出。
优点:使用两段式状态机,其描述过程都是时序电路。而时序电路可以很好的避免毛刺的产生。
4.设计案例:使用状态机实现一个简单的自动售货机功能
题目描述:售货机中商品2.5元一件,每次投币为3元、1元、0.5元,需要设定找零。
-
状态转移图
这里一共设置了五个状态,当投入钱数足够时,直接回到idle状态。
除了在idle状态需要考虑投入3元钱,其他状态只需要考虑输入1元、0.5元或者没有输入。所以ABCD状态均有三个输出箭头,即三种状态迁移。
-
代码编写
状态编码使用独热码,减少时序问题
对于输入money和输出change,当值为2时代表0.5元,1代表1元,3代表3元。
module fsm(
input wire clk,
input wire rst_n,
input wire [1:0] money,// 1=1,2=0.5,3=3
output reg cola,
output reg [1:0] change// 1=1,2=0.5,3=3
);
reg [4:0] state;
parameter idle = 5'b00001;
parameter A = 5'b00010;
parameter B = 5'b00100;
parameter C = 5'b01000;
parameter D = 5'b10000;
//第一个always块描述状态转移
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= idle;
end
else begin
case(state)
idle: if (money == 2'd2) state <= A;
else if (money == 2'd1) state <= B;
else if (money == 2'd3) state <= idle;
else state <= state;
A: if (money == 2'd2) state <= B;
else if (money == 2'd1) state <= C;
else state <= state;
B: if (money == 2'd2) state <= C;
else if (money == 2'd1) state <= D;
else state <= state;
C: if (money == 2'd2) state <= D;
else if (money == 2'd1) state <= idle;
else state <= state;
D: if (money == 2'd2) state <= idle;
else if (money == 2'd1) state <= idle;
else state <= state;
default: state <= idle;
endcase
end
end
//第二个always块描述输出
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cola <= 1'd0;
change <= 2'd0;
end
else if ( state == C && money == 2'd1 ) begin
cola <= 1'd1;
change <= 2'd0;
end
else if ( state == D && money == 2'd1 ) begin
cola <= 1'd1;
change <= 2'd2;
end
else if ( state == D && money == 2'd2 ) begin
cola <= 1'd1;
change <= 2'd0;
end
else if ( state == idle && money == 2'd3 ) begin
cola <= 1'd1;
change <= 2'd2;
end
else begin
cola <= 1'd0;
change <= 2'd0;
end
end
endmodule
- 编写testbench进行结果仿真
module tb_fsm();
reg clk;
reg rst_n;
reg [1:0] money;
wire cola;
wire[1:0] change;
initial begin
clk = 0;
rst_n = 0;
money = 0;
#100
rst_n = 1;
end
always begin #10 clk = ~clk; end
always begin #20 money = {$random}; end
fsm inst_fsm (
.clk (clk),
.rst_n (rst_n),
.money (money),
.cola (cola),
.change (change)
);
endmodule
仿真结果分析
5.使用tcl脚本进行仿真
注意:结构体使用时,两个virtual后面的type、function,前后都有空格
quit -sim
#退出上一次仿真
.main clear
#清除保留的临时文件
vlib work
#创建work的库
vlog ./tb_fsm.v
#添加编译文件
vlog ./../design/*.v
#..返回上一级文件夹
vsim -voptargs=+acc work.tb_fsm
#启动tb顶层仿真
#创建一个结构体,仿真结果可以替换显示
virtual type {
{5'b00001 idle}
{5'b00010 one}
{5'b00100 two}
{5'b01000 three}
{5'b10000 four}
} replace;
virtual function {(replace)/tb_fsm/inst_fsm/state} new_state;
#添加波形
add wave /tb_fsm/inst_fsm/*
run 1us