1.状态机的设计思路:
一是从状态变量入手,分析各个状态的输入,状态转移和输出
二是先确定电路的输出关系,再回溯规划每个状态的条件,输入等
2.状态机的三要素是状态,输入和输出,根据状态机状态是否和输入条件相关,可以分为摩尔型状态机(与输入条件相关)和米勒型状态机(与输入无关)
3.一段式描述:将用于状态转移判断的组合逻辑和用于状态寄存器转移的时序逻辑写在同一个always里面,在描述当前状态时还要考虑下一个状态的输出(这样写代码不清晰,难以维护)
二段式描述:输出使用的是组合逻辑,很容易产生毛刺等不稳定因素
三段式描述:根据对下一状态的判断,利用同步时序逻辑来寄存状态机的输出,从而消除了组合逻辑输出的不稳定性和毛刺的隐患,有利于时序路径分组
4.以自动售卖机为例:
一段式:
module auto_sell1(
input clk,
input rst_n,
input half, //0.5元
input one, //1元
output reg drink, //饮料售价2.5元,售出饮料信号
output reg flag //找零信号
);
reg [2:0] state; //投币状态
parameter s0 = 3'b000; //无投币
parameter s1 = 3'b001; //投币0.5
parameter s2 = 3'b010; //投币1
parameter s3 = 3'b011; //投币1.5
parameter s4 = 3'b100; //投币2
parameter s5 = 3'b101; //投币2.5 拉高drink信号
parameter s6 = 3'b110; //找零
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
drink <= 0;
flag <= 0;
state <= s0;
end
else
begin
case(state)
s0: begin //当为起始状态时,去判断有没有钱进来
if(half == 1'b1) //当有0.5进时 则进入s1状态
state <= s1;
else if(one == 1'b1)//如果是直接进的1元,则进入s2状态
state <= s2;
else //否则就是没有钱进入,停留在s0状态
state <= s0;
end
s1: begin //此时已经有0.5元了,判断是否有0.5进入,有的话进入s2状态
if(half == 1'b1)
state <= s2;
else if(one == 1'b1) //如果此时有投入1元 则进入s3
state <= s3;
else //否则还是只有0.5 停在该状态
state <= s1;
end
s2: begin //此时已经有1元
if(half == 1'b1) //如果又投入0.5,进入s3
state <= s3;
else if(one == 1'b1)
state <= s4;
else
state <= s2;
end
s3: begin //此时已经有1.5元
if(half == 1'b1)
state <= s4;
else if(one == 1'b1)
state <= s5;
else
state <= s3;
end
s4: begin //此时已经有2元
if(half == 1'b1)
state <= s5;
else if(one == 1'b1)
state <= s6;
else
state <= s4;
end
s5: begin //此时已经有2.5元
drink <= 1;
state <= s0;
end
s6: begin //此时已经有3元
drink <= 1;
flag <= 1;
state <= s0;
end
default : state <= s0;
endcase
end
end
endmodule
5.二段式:
module auto_sell2(
input clk,
input rst_n,
input half, //0.5元
input one, //1元
output reg drink, //饮料售价2.5元,售出饮料信号
output reg flag //找零信号
);
reg [2:0] c_state; //投币状态
reg [2:0] n_state;
parameter s0 = 3'b000; //无投币
parameter s1 = 3'b001; //投币0.5
parameter s2 = 3'b010; //投币1
parameter s3 = 3'b011; //投币1.5
parameter s4 = 3'b100; //投币2
parameter s5 = 3'b101; //投币2.5 拉高drink信号
parameter s6 = 3'b110; //找零
//第一段
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
c_state<= s0;
else
c_state<= n_state;
end
//第二段
always @(*)
begin
if(!rst_n)
begin
drink <= 0;
flag <= 0;
n_state <= s0;
end
else
begin
case(c_state)
s0: begin //当为起始状态时,去判断有没有钱进来
if(half == 1'b1) //当有0.5进时 则进入s1状态
n_state <= s1;
else if(one == 1'b1)//如果是直接进的1元,则进入s2状态
n_state <= s2;
else //否则就是没有钱进入,停留在s0状态
n_state <= s0;
end
s1: begin //此时已经有0.5元了,判断是否有0.5进入,有的话进入s2状态
if(half == 1'b1)
n_state <= s2;
else if(one == 1'b1) //如果此时有投入1元 则进入s3
n_state <= s3;
else //否则还是只有0.5 停在该状态
n_state <= s1;
end
s2: begin //此时已经有1元
if(half == 1'b1) //如果又投入0.5,进入s3
n_state <= s3;
else if(one == 1'b1)
n_state <= s4;
else
n_state <= s2;
end
s3: begin //此时已经有1.5元
if(half == 1'b1)
n_state <= s4;
else if(one == 1'b1)
n_state <= s5;
else
n_state <= s3;
end
s4: begin //此时已经有2元
if(half == 1'b1)
n_state <= s5;
else if(one == 1'b1)
n_state <= s6;
else
n_state <= s4;
end
s5: begin //此时已经有2.5元
drink <= 1;
n_state <= s0;
end
s6: begin //此时已经有3元
drink <= 1;
flag <= 1;
n_state <= s0;
end
default : n_state <= s0;
endcase
end
end
endmodule
6.三段式:
module auto_sell3(
input clk,
input rst_n,
input half, //0.5元
input one, //1元
output reg drink, //饮料售价2.5元,售出饮料信号
output reg flag //找零信号
);
reg [2:0] c_state; //投币状态
reg [2:0] n_state;
parameter s0 = 3'b000; //无投币
parameter s1 = 3'b001; //投币0.5
parameter s2 = 3'b010; //投币1
parameter s3 = 3'b011; //投币1.5
parameter s4 = 3'b100; //投币2
parameter s5 = 3'b101; //投币2.5 拉高drink信号
parameter s6 = 3'b110; //找零
//第一段
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
c_state<= s0;
else
c_state<= n_state;
end
//第二段
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
drink <= 0;
flag <= 0;
end
else
begin
case(c_state)
s0: begin //当为起始状态时,去判断有没有钱进来
drink <= 0;
flag <= 0;
end
s1: begin //此时已经有0.5元了,判断是否有0.5进入,有的话进入s2状态
drink <= 0;
flag <= 0;
end
s2: begin //此时已经有1元
drink <= 0;
flag <= 0;
end
s3: begin //此时已经有1.5元
drink <= 0;
flag <= 0;
end
s4: begin //此时已经有2元
drink <= 0;
flag <= 0;
end
s5: begin //此时已经有2.5元
drink <= 1;
flag <= 0;
end
s6: begin //此时已经有3元
drink <= 1;
flag <= 1;
end
default : n_state <= s0;
endcase
end
end
always @(*)
begin
if(!rst_n)
n_state <= s0;
else
begin
case(c_state)
s0: begin //当为起始状态时,去判断有没有钱进来
if(half == 1'b1) //当有0.5进时 则进入s1状态
n_state <= s1;
else if(one == 1'b1)//如果是直接进的1元,则进入s2状态
n_state <= s2;
else //否则就是没有钱进入,停留在s0状态
n_state <= s0;
end
s1: begin //此时已经有0.5元了,判断是否有0.5进入,有的话进入s2状态
if(half == 1'b1)
n_state <= s2;
else if(one == 1'b1) //如果此时有投入1元 则进入s3
n_state <= s3;
else //否则还是只有0.5 停在该状态
n_state <= s1;
end
s2: begin //此时已经有1元
if(half == 1'b1) //如果又投入0.5,进入s3
n_state <= s3;
else if(one == 1'b1)
n_state <= s4;
else
n_state <= s2;
end
s3: begin //此时已经有1.5元
if(half == 1'b1)
n_state <= s4;
else if(one == 1'b1)
n_state <= s5;
else
n_state <= s3;
end
s4: begin //此时已经有2元
if(half == 1'b1)
n_state <= s5;
else if(one == 1'b1)
n_state <= s6;
else
n_state <= s4;
end
s5: begin //此时已经有2.5元
n_state <= s0;
end
s6: begin //此时已经有3元
n_state <= s0;
end
default : n_state <= s0;
endcase
end
end
endmodule