1.原理
同步的意思就是状态的跳转都是在时钟的作用下跳转的,有限是指状态机中状态的个数是有限的。两种状态机的共同点都是状态的跳转只和输入有关,区别就是如果最后的输出只和当前状态有关而与输入无关,则是moore型状态机。如果最后的输出不仅和当前状态有关还和输入有关就称为mealy型状态机。
状态机的每一个状态代表一个事件。我们需要做的就是执行该事件,然后跳转到下一事件。状态机特别适合描述那些事情发生有先后顺序或者是时序归类的事情。
2.实战
实现一个简单的状态机,可乐售卖机,每次只能投一枚硬币,总共需要3元,可乐就会出来。
状态图有3要素,输入、输出、状态。
这两种状态转移图都是正确的,状态跳转的条件是输入一枚硬币,输出0代表可乐还未出来,1代表可乐可以出来。
可以看出右图最后的输出(1)只和当前的状态有关所以是moore型状态机,左图最后的输出除了和当前状态有关还和输入有关所以是mealy型状态机。
一般我们都喜欢化到最简。这里以mealy型状态机为例说明
使用独热码进行编码,把3个比特位的比较器变成了一个比特位的比较器,节省了组合逻辑的资源,代价就是需要的位宽变多了。在FPGA中,组合逻辑的资源是比较少的,但寄存器资源比较多。而二进制编码的利弊恰好和独热码相反,它用到的寄存器资源较少,而组合逻辑资源较多。综合的时候,无法进行比较器的优化。因为综合器对独热码的比较器进行了优化,所以用独热吗编写的代码可以在高速系统中运行。但是多个比特位的比较器,每次进行比较的时候,每个比特位到达比较器的时间会因为布局布线的走线长短而导致延时的不同,这样就会导致输出的不稳定或者是不准确。但是单比特的比较器就不需要考虑这个问题。如果状态数非常多,FPGA也吃不消对寄存器的消耗。所以说当状态数比较多的时候就使用格雷码,格雷码相近的两个状态之间只有1位数的不同。格雷码虽然也和二进制编码一样需要的位数比较少,使用组合逻辑比较多,但是相邻状态转换时只有1个状态是发生翻转的。这样不仅能消除状态转换时由多条信号线的传输延时造成的毛刺,又可以降低功耗。相当于独热码和2进制码的一个折中。
总结:在FPGA低速系统中,如果状态机中状态的个数小于4,就使用2进制码。如果状态数介于4-24之间,就使用独热码。如果状态的个数大于24个,就使用格雷码。在高速系统中无论有多少个状态,都推荐使用独热码。
2.1 simple_fsm.v
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
以上是2段式的状态机编码。
其余资料上的状态机编码有1段式,2段式,3段式。1段式状态机就是在一段的状态机中使用时序逻辑既描述状态的转移,又描述数据的输出。2段式表示份两段状态机,在第一段状态机中使用时序逻辑描述状态的转移,在第二段状态机中使用组合逻辑描述数据的输出。3段式是指在第1段采用时序逻辑描述状态的转移,在第2段中采用组合逻辑判断状态转移条件,描述状态转移规律,在第3段状态机中描述状态输出,第3段的描述可以使用组合逻辑也可以使用时序逻辑。
1段式在描述大型的系统是比较困难的,会使系统十分臃肿,不利于理解和修改。2段式简洁,但是第2段是用组合逻辑描述的,有些情况可能无法表达,比如说,输出时,需要计数的累加情况,这种情况会在组合逻辑中产生自迭代,自迭代在组合逻辑中是被禁止的。而且第二段的作用主要是使用组合逻辑描述数据的输出,输出使用组合逻辑可能会产生许多的毛刺。所以说也不推荐。
3段式是指在第1段采用时序逻辑描述状态的转移,在第2段中采用组合逻辑判断状态转移条件,描述状态转移规律,在第3段状态机中描述状态输出,第3段的描述可以使用组合逻辑也可以使用时序逻辑。最新的两段式就是把1,2部整合,用第一段时序逻辑既描述当前的状态又用组合逻辑描述下一状态。第二段保持不变。这是最推荐的。
综合器产生的状态转移图
2.2 tb文件
`timescale 1ns/1ns
module tb_simple_fsm();
reg sys_clk;
reg sys_rst_n;
reg pi_money;
wire po_cola;
wire [2:0] state=simple_fsm_inst.state;
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;
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
以上是tb文件的代码
RTL视图