简易状态机simple_fsm
实验目的
实现一个简易状态机,实现一个可乐售卖机,达到三块钱一瓶,实现投入一元硬币实现输出可乐的状态。
实验框图与波形
状态图
由上图所示的状态图,可以更直观形象地表示出时序电路运行中的全部状态、各状态间相互转换的关系以及转换的条件和结果。图中每一个圆圈都对应着一个状态,圆圈中标出状态名;每个带箭头的方向线都表示一个转换,箭头指示出状态转换的方向。引起该状态转换的输入变量逻辑值标再方向线旁斜线左侧,输出变量的逻辑值标在方向线旁斜线的右侧。
二进制码、格雷码和独热码
格雷码
在一组数的编码中,若任意两个相邻的代码只有一位二进制数不同,则称这种编码为格雷码(Gray Code),另外由于最大数与最小数之间也仅一位数不同,即“首尾相连”,因此又称循环码或反射码。
当状态多时,采用格雷码,介于二进制码与独热码的折中
parameter IDLE = 2'b00;
parameter ONE = 2'b01;
parameter TWO = 2'b11;
独热码
独热编码即 One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。虽然使用较多的触发器,但由于状态译码简单,可减少组合逻辑且速度较快, 这种编码方式还易于修改,增加状态或改变状态转换条件都可以在不影响状态机的其它部分的情况下很方便地实现。另外,它的速度独立于状态数量。与之相比,压缩状态编码在状态增加时速度会明显下降。
FPGA中组合逻辑资源较少,寄存器资源较多,独热码编码状态机可以在高速下运行
parameter IDLE = 3'b001;
parameter ONE = 3'b010;
parameter TWO = 3'b100;
二进制码
二进制码占用资源刚好与独热码相反,会在高速运行下产生不稳定效果
parameter IDLE = 2'b00;
parameter ONE = 2'b01;
parameter TWO = 2'b10;
**二进制编码、格雷码编码使用最少的触发器,消耗较多的组合逻辑,而独热码编码反之。**独热码编码的最大优势在于状态比较时仅仅需要比较一个位,从而一定程度上简化了译码逻辑。虽然在需要表示同样的状态数时,独热编码占用较多的位,也就是消耗较多的触发器,但这些额外触发器占用的面积可与译码电路省下来的面积相抵消。
为了进一步提高独热编码的速度,可以使用并行 CASE语句"即在case(1‘b1) 后添加综合器可以辨认的并行CASE注释语句。注意:并行CASE 只推荐在独热编码时使用,在二进制编码和格雷编码时使用有时反而会增大面积降低速度。
在CPLD中,由于器件拥有较多的地提供组合逻辑资源,所以CPLD多使用二进制编码或格雷码,而FPGA更多地提供触发器资源,所以在FPGA中多使用独热码编码。当然,这并不是说在FPGA中就非得用独热编码,在CPLD中不能用独热编码,一般的,对于小型设计(状态数小于4)使用二进制编码,当状态数处于4-24之间时,宜采用独热码编码,而大型状态机(状态数大于24)使用格雷码更高效。
case语句
第一段描述状态变化第二段赋值
形式:
case(控制表达式/值)
分支表达式:执行语句
default:执行语句
endcase
功能:
自上而下,按照顺序逐个对分支表达式进行判断,如果这一分支表达式等于控制表达式的值,就执行其对应操作;均不相等时,执行default操作;
注意:
分支表达式不能重复,否则会出现冲突;
执行完某一操作后,跳出case语句;
控制表达式与多个分支表达式匹配,只执行从上至下首个匹配项(判断顺序进行,执行一次后就跳出case语句了);
新式二段式,第一段描述状态变化第二段赋值!!!!!!重要语法
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
实验代码
module simple_fsm
(
input wire sys_clk,
input wire sys_rst_n,
input wire 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)
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
仿真代码
`timescale 1ns/1ns
module tb_simple_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;
#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
对某一模块变量进行监测
对模块内部变量进行检测,采用该语句
wire [2:0] state=simple_fsm_inst.state;//simple_fsm_inst中的state通过.连接,这样可以在仿真代码中读取内部变量进行检测