状态机类型
Moore 状态机
Moore 型状态机的输出只与当前状态有关,与当前输入无关。输入对输出的影响要到下一个时钟周期才能反映出来。
Mealy 状态机。
Mealy 型状态机的输出,不仅与当前状态有关,还取决于当前的输入信号。输入变化可能出现在任何状态的时钟周期内
Mealy型3段式状态机 自动售卖机:
状态转移图:
自动售卖机的功能描述如下:
饮料单价 2 元,该售卖机只能接受 0.5 元、1 元的硬币。考虑找零和出货。投币和出货过程都是一次一次的进行,不会出现一次性投入多币或一次性出货多瓶饮料的现象。每一轮售卖机接受投币、出货、找零完成后,才能进入到新的自动售卖状态。
其中,coin = 1 代表投入了 0.5 元硬币,coin = 2 代表投入了 1 元硬币。
// 售卖机 三段式状态机
module vending_machine_p3 (
input clk,
input rstn,
input [1:0] coin, // 投入硬币 01 for 0.5 jiao, 10 for 1 yuan
output [1:0] change, // 找零
output sell // 出货
);
// 状态码
parameter IDLE = 3'd0;
parameter GET05 = 3'd1; // 收了5毛
parameter GET10 = 3'd2; // 收了1元
parameter GET15 = 3'd3; // 收了1.5元
reg [2:0] next_state;
reg [2:0] current_state;
// 状态机第一段,时序逻辑,非阻塞赋值,传递寄存器的状态
always @(posedge clk or negedge rstn) begin
if(~rstn) begin
next_state <= 3'b0;
current_state <= 3'b0;
end
else begin
current_state <= next_state;
end
end
// 状态机第二段,组合逻辑,阻塞赋值,根据当前状态和当前输入,确定下一个状态机的状态。
always @(*) begin
case(current_state)
IDLE:
case(coin)
2'b01: next_state = GET05; // 收了5毛
2'b10: next_state = GET10; // 收了1元
default:next_state = IDLE; // 原地不动
endcase
GET05:
case(coin)
2'b01: next_state = GET10; // 收了1元
2'b10: next_state = GET15; // 收了1.5元
default:next_state = GET05; // 原地不动
endcase
GET10:
case(coin)
2'b01: next_state = GET15; // 收了1.5元
2'b10: next_state = IDLE; // 收了2元,达到单价,返回
default:next_state = GET10; // 原地不动
endcase
GET15:
case(coin)
2'b01: next_state = IDLE; // 收了2元,达到单价,返回
2'b10: next_state = IDLE; // 收了2.5元,超过单价,找零,返回
default:next_state = GET15; // 原地不动
endcase
default:
next_state = IDLE;
endcase // current_state
end
// 状态机第三段,时序逻辑,非阻塞赋值,因为是 Mealy 型状态机,根据当前状态和当前输入,确定输出信号
reg [1:0] change_r;
reg sell_r;
always @(posedge clk or negedge rstn) begin
if(~rstn) begin
change_r <= 2'b0 ;
sell_r <= 1'b0;
end
else if ((current_state == GET15 && coin == 2'd1 )
|| (current_state == GET10 && coin == 2'd2 ))begin
change_r <= 2'b0 ;
sell_r <= 1'b1; // 出货
end
else if (current_state == GET15 && coin == 2'd2 )begin
change_r <= 2'b1 ; // 找零
sell_r <= 1'b1; // 出货
end
else begin // default
change_r <= 2'b0 ;
sell_r <= 1'b0;
end
end
assign sell = sell_r;
assign change = change_r;
endmodule
`timescale 1ns/1ps
module vending_machine_p3_tb ;
reg clk;
reg rstn;
reg [1:0] coin; // 投入硬币 01 for 0.5 jiao, 10 for 1 yuan
wire [1:0] change; // 找零
wire sell; // 出货
//(1) mealy state with 3-stage
vending_machine_p3 u_mealy_p3 (
.clk(clk),
.rstn(rstn),
.coin(coin),
.change(change),
.sell(sell)
);
// 仿真停止
always begin
#100;
if ($time >= 10000) $finish ;
end
parameter CYCLE_200MHz = 10; // 1/200*10^6 = 5*10^-9 = 5ns 半周期
always begin
clk = 0 ; #(CYCLE_200MHz/2) ; // 半周期翻转#(CYCLE_200MHz/2)
clk = 1 ; #(CYCLE_200MHz/2) ;
end
reg [9:0] buy_oper; // 操作状态
initial begin
buy_oper = 10'd0;
coin = 2'd0;
rstn = 1'd0;
#8 rstn = 1'b1; // (即仿真开始后的第8ns,第一个周期内)(一个周期10ns)
@(negedge clk); // 从下降沿开始(即仿真开始后的第10ns)
//case(1) 0.5 -> 0.5 -> 0.5 -> 0.5
#16; // 等待16ns (即仿真开始后的第26ns,第3个周期内)
buy_oper = 10'b00_0101_0101 ;
repeat(5) begin
@(negedge clk); // 再过4ns到下降沿 (即仿真开始后的第30ns)
coin = buy_oper[1:0];
buy_oper = buy_oper >> 2; // 右移两位 (每次都给5毛)
end
//case(2) 1 -> 0.5 -> 1
#16; // 再过16ns (即仿真开始后的第46ns,clk=1在45ns-50ns之间)
buy_oper = 10'b00_0010_0110 ;
repeat(5) begin
@(negedge clk); // 再过4ns到下降沿 (即仿真开始后的第50ns)
coin = buy_oper[1:0];
buy_oper = buy_oper >> 2; // 右移两位 (每次都给5毛)
end
//case(3) 0.5 -> 1 -> 0.5
#16 ;
buy_oper = 10'b00_0001_1001 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
//case(4) 0.5 -> 0.5 -> 0.5 -> 1
#16 ;
buy_oper = 10'b00_1001_0101 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
end
endmodule // test