状态机:描述时序系统的状态,简称为FSM(Finite State Machine),全称同步有限状态机。同 步指状态的变化只发生在时钟的上升沿或下降沿,有限指状态的数量有限。
分类:Moore型、Mealy型。
Moore型:输出只与状态有关,与输入无关。
Mealy型:输出既与状态有关,又与输入有关。
一个简单的例子:自助可乐贩卖机
一台贩卖售价为3元的可乐贩卖机,每次可投入1元硬币,投入三枚时出一瓶可乐。
首先分析它的输入输出和状态
输入:是否投入1元硬币(0表示没投,1表示投了)
输出:是否出可乐(0表示不出,1表示出)
状态:初始状态、有1元、有2元、有3元(等效于初始状态)
然后分析状态转换条件(画出状态转换图)
最后根据状态转换图编程、调试
程序及仿真代码
module simple_fsm (
input wire sys_clk ,
input wire sys_rst_n ,
input wire pi_one ,
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) begin
if(sys_rst_n == 1'b0)
state <= IDLE;
else case (state)
IDLE : state <= (pi_one == 1'b1 ? ONE : IDLE);
ONE : state <= (pi_one == 1'b1 ? TWO : ONE );
TWO : state <= (pi_one == 1'b1 ? IDLE : TWO );
default : state <= IDLE;
endcase
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
po_cola <= 1'b0;
else if((state == TWO) && (pi_one == 1'b1))
po_cola <= 1'b1;
else
po_cola <= 1'b0;
end
endmodule //simple_fsm
module tb_simple_fsm ();
reg sys_clk;
reg sys_rst_n;
reg pi_one;
wire po_cola;
wire [2:0] state = simple_fsm_inst.state;
initial begin
sys_clk = 1'b0;
sys_rst_n <= 1'b0;
pi_one <= 1'b0;
#10
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 == 0)
pi_one <= 1'b0;
else
pi_one <= {$random} % 2;
initial begin
$dumpfile("simple_fsm.vcd");
$dumpvars;
#2000 $finish;
end
simple_fsm simple_fsm_inst(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.pi_one (pi_one),
.po_cola (po_cola)
);
endmodule //tb_simple_fsm
波形图
一个稍复杂的例子:自助可乐贩卖机
一台贩卖售价为2.5元的可乐贩卖机,每次可投入1元硬币或5角硬币,投入超过2.5元时出一瓶可乐,多出部分找零。
首先分析它的输入输出和状态
输入:是否投入1元硬币(0表示没投,1表示投了)、是否投入5角硬币(0表示没投,1表示投了),注意由于投币口只有一个,无法同时投入1元与5角硬币。
输出:是否出可乐(0表示不出,1表示出)、是否找零(0表示不找,1表示找)
状态:初始状态、有0.5元、有1元、有1.5元、有2元
然后分析状态转换条件(画出状态转换图)
最后根据状态转换图编程、调试
程序及仿真代码
module complex_fsm (
input wire sys_clk ,
input wire sys_rst_n ,
input wire pi_one ,
input wire pi_half ,
output reg po_cola ,
output reg po_money
);
wire [1:0] pi_money;
assign pi_money = {pi_half,pi_one};
parameter IDLE = 5'b00001,
HALF = 5'b00010,
ONE = 5'b00100,
ONE_HALF= 5'b01000,
TWO = 5'b10000;
reg [4:0] state;
always@(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
state <= IDLE;
else case (state)
IDLE :
if(pi_money == 2'b10)
state <= HALF;
else if(pi_money == 2'b01)
state <= ONE;
else
state <= IDLE;
HALF :
if(pi_money == 2'b10)
state <= ONE;
else if(pi_money == 2'b01)
state <= ONE_HALF;
else
state <= HALF;
ONE :
if(pi_money == 2'b10)
state <= ONE_HALF;
else if(pi_money == 2'b01)
state <= TWO;
else
state <= ONE;
ONE_HALF:
if(pi_money == 2'b10)
state <= TWO;
else if(pi_money == 2'b01)
state <= IDLE;
else
state <= ONE_HALF;
TWO :
if(pi_money == 2'b10)
state <= IDLE;
else if(pi_money == 2'b01)
state <= IDLE;
else
state <= TWO;
default : state <= IDLE;
endcase
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
po_cola <= 1'b0;
else if((state == TWO) && (^pi_money == 1'b1))
po_cola <= 1'b1;
else if((state == ONE_HALF) && (pi_one == 1'b1))
po_cola <= 1'b1;
else
po_cola <= 1'b0;
end
always@(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
po_money <= 1'b0;
else if((state == TWO) && (pi_one == 1'b1))
po_money <= 1'b1;
else
po_money <= 1'b0;
end
endmodule //complex_fsm
module tb_complex_fsm ();
reg sys_clk;
reg sys_rst_n;
reg pi_one;
reg pi_half;
wire po_cola;
wire po_money;
wire [4:0] state = complex_fsm_inst.state;
initial begin
sys_clk = 1'b0;
sys_rst_n <= 1'b0;
pi_one <= 1'b0;
pi_half <= 1'b0;
#10
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 == 0)
{pi_half,pi_one} <= 2'b0;
else
{pi_half,pi_one} <= {$random} % 3;
initial begin
$dumpfile("complex_fsm.vcd");
$dumpvars;
#2000 $finish;
end
complex_fsm complex_fsm_inst(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.pi_one (pi_one),
.pi_half (pi_half),
.po_cola (po_cola),
.po_money (po_money)
);
endmodule //tb_complex_fsm
波形图
写代码时需要注意的地方:
在描述状态时,有三种编码方式:独热码、二进制码、格雷码。
编码方式 | 定义 | 特点 | |||
独热码 | 有几个状态就用几个比特 每一个状态对应一个比特位与其他比特位不同 例如: 状态一:001 状态二:010 状态三:100 | 废空间,省时间 | |||
二进制码 | 用二进制数表示状态 例如: 状态一:00 状态二:01 状态三:10 | 省空间,费时间 | |||
格雷码 | 类似二进制码,但相邻状态只改变一个比特位 例如: 状态一:00 状态二:01 状态三:11 | 介于中间态 |
FPGA的特性更利于使用独热码。
FPGA编程下,建议在低速机中,状态数小于4时使用二进制码,4~24之间使用独热码,大于24时使用格雷码;高速机中一律使用独热码。