状态机
首先得了解FPGA为什么需要状态机。FPGA的模块都是并行执行,那么如果需要处理具有前后顺序的事件,这里就要使用到状态机。
状态机分为摩尔型状态机(Moore)和米莉型状态机(mealy)。他们的共同点是状态的跳转和输入有关。区别就是当最后的输出是只与当前状态有关,与输入无关,则属于莫尔型状态机。若最后的输出不仅和当前状态有关还与输入有关,则是米莉型状态机。
这里我们用verilog实现一个可乐机。要求是可乐机每次只能投入一元,且每瓶可乐卖三元。
输入/输出
根据实验要求,可将状态机分为四个状态,ZERO,ONE,TWO,THREE。输入分为零元,一元。输出分为出可乐和不出可乐。初始状态为ZERO,当投入零元时,保持状态不变,当投入一元时,跳到ONE状态,以此类推,最后到THREE状态且输出可乐。由图可知,次状态机为摩尔型状态机,最终输出状态与输入无关。而米莉型状态机图片则如下图。
由图状态可知,当输出可乐时,不仅和当前状态有关,还与输入有关。这里就是两种状态机的区别。
RTL代码
module coke_machine
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire money_in ,//投钱,1表示投入一元,0表示投入零元
output reg coke //1表示出可乐,0表示不出可乐
);
reg [2:0] state;
//这里采用独热码的编码方式表示三个状态
parameter ZERO = 3'd100,
ONE = 3'd010,
TWO = 3'd001;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= ZERO;
else case(state)
ZERO :if(money_in == 1'b1)
state <= ONE;
else
state <= ZERO;
ONE :if(money_in == 1'b1)
state <= TWO;
else
state <= ONE;
TWO :if(money_in == 1'b1)
state <= ZERO;
else
state <= TWO;
default : state <= ZERO;
endcase
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
coke <= 1'b0;
else if(state == TWO && money_in == 1'b1)
coke <= 1'b1;
else
coke <= 1'b0;
endmodule
状态机视图
在编写代码过程中,这里采用了独热码来表示不同的状态。我们可以也可以采用二进制码或者格雷码。独热码,每个状态只有一个bit是高电平。二进制码就是00,01,10,11,换成十进制就是0,1,2,3,依次累加。格雷码在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,例如00,01,11,10。独热码因为每个状态只有 1bit 是不同的,在适合条件下综合时相对于其他编码会更加节省组合逻辑资源。因为FPGA中组合逻辑资源较少,比较宝贵。而寄存器资源多。
所以在不同条件下使用编码时,可以参考下图
testbench
`timescale 1ns/1ns
module tb_coke_machine();
reg sys_clk;
reg sys_rst_n;
reg money_in;
wire coke;
initial
begin
sys_clk = 1'b0;
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)
money_in <= 1'b0;
else
money_in <= {$random} % 2;//随机产生一个二进制数
coke_machine coke_machine_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.money_in (money_in),//投钱,1表示投入一元,0表示投入零元
.coke (coke)//1表示出可乐,0表示不出可乐
);
endmodule
modelsim仿真结果