原文链接:https://blog.csdn.net/qq_34070723/article/details/100737225
作者:King阿金
目录
1.状态机原理与三段式状态机
MOORE 与 MEALEY 状态机的特征?
Moore 状态机的输出仅与当前状态值有关, 且只在时钟边沿到来时才会有状态变化。次态=f(现状,输入),输出=f(现状)
Mealy 状态机的输出不仅与当前状态值有关, 而且与当前输入值有关。次态=f(现状,输入),输出=f(现状,输入)
描述同一个事务,mealy的状态更少。
通用mealy三段式状态机(建议实际工程中采用的结构)
-
//------采用独热码或格雷码或其他方式编码状态
-
parameter
IDLE = ...
-
parameter
S0 = ...
-
...
-
reg [x:
0] curr_state;
-
reg [x:
0] next_state;
-
-
//------每个时钟只产生一次状态变化
-
always @(posedge clk or posedge asy_rst)
-
begin
-
if(asy_rst) curr_state <=
IDLE ;
-
else curr_state <= next_state;
-
end
-
-
//------产生的下一状态的组合逻辑
-
always @(*)
-
begin
-
next_state = 'bx;
-
case(curr_state)
-
IDLE:
if(输入) next_state =
S0 ;
else...;
-
S0:
if(输入) next_state = ...;
-
...
-
default: next_state = ...;
-
endcase
-
end
-
/************************时序或组合二选一***********************/
-
//------时序逻辑输出(比组合逻辑延时一个时钟)
-
always@(posedge clk or posedge asy_rst)
-
begin
-
if(asy_rst)out<= ...;
-
else
-
case(curr_state)
-
sO:
if(...) out <= ...;
-
sO:
if(...) out <= ...;
-
default: out <= ...;
-
endcase
-
end
-
//------组合逻辑输出 采用 assign 或always
-
always @(*)
-
begin
-
if(asy_rst)out = ...;
-
else
-
case(curr_state)
-
sO:
if(...) out = ...;
-
sO:
if(...) out = ...;
-
default: out = ...;
-
endcase
-
end
-
/***********************************************************/
2.状态机示例
2.1自动饮料售卖机/卖报机
题目:商品是5分钱,只能投3种硬币:1分,2分,5分,考虑找零。
思路1:采用状态机,共分为5个状态,S0-S4代表已经投入的钱,画出状态转换图(略),采用三段式mealy状态机。
思路2:(更简单)不采用状态机,直接对输入的钱进行加法运算,多余5分就输出和找零,同时将内部加法器清零。
另一个版本:用Verilog实现接受0.5元,1元的可乐售卖机,单价2.5元,考虑找零和出货。
-
module auto_sell_better
#(
-
parameter I_WD =
4 , O_WD =
4
-
)(
-
input clk,
-
input asy_rst,
-
input [I_WD
-1:
0] i_dat,
-
input i_val,
-
-
output reg [O_WD
-1:
0] o_money,
//找零
-
output reg o_goods
//商品
-
);
-
-
parameter S0=
4
'b0000;
-
parameter S1=4'b0001;
//1分钱
-
parameter S2=
4
'b0010;
-
parameter S3=4'b0100;
-
parameter S4=
4
'b1000; //4分钱
-
-
reg [3:0] curr_s;
-
reg [3:0] next_s;
-
//每个时钟只产生一次状态变化
-
always @(posedge clk or posedge asy_rst)
-
begin
-
if(asy_rst)
-
curr_s <= S0;
-
else
-
curr_s <= next_s;
-
end
-
-
//产生的下一状态的组合逻辑
-
always @(*)
-
begin
-
next_s = 'dx;
-
case(curr_s)
-
S0:
if(i_dat ==
1) next_s = S1;
-
else
if(i_dat ==
2) next_s = S2;
-
else
if(i_dat ==
5) next_s = S0;
-
else next_s = S0;
-
S1:
if(i_dat ==
1) next_s = S2;
-
else
if(i_dat ==
2) next_s = S3;
-
else
if(i_dat ==
5) next_s = S0;
-
else next_s = S1;
-
S2:
if(i_dat ==
1) next_s = S3;
-
else
if(i_dat ==
2) next_s = S4;
-
else
if(i_dat ==
5) next_s = S0;
-
else next_s = S2;
-
S3:
if(i_dat ==
1) next_s = S4;
-
else
if(i_dat ==
2) next_s = S0;
-
else
if(i_dat ==
5) next_s = S0;
-
else next_s = S3;
-
S4:
if(i_dat ==
1) next_s = S0;
-
else
if(i_dat ==
2) next_s = S0;
-
else
if(i_dat ==
5) next_s = S0;
-
else next_s = S4;
-
default: next_s = S0;
-
endcase
-
end
-
/**************************时序逻辑的输出*******************************/
-
always @(posedge clk
or posedge asy_rst)
-
begin
-
if(asy_rst)
-
begin
-
o_money <=
0;
-
o_goods <=
0;
-
end
-
else
-
case(curr_s)
-
S0:
-
begin
-
o_money <=
0;
-
if(i_dat==
5) o_goods <=
1;
-
else o_goods <=
0;
-
end
-
S1:
-
begin
-
if(i_dat==
5) begin o_money <=
1; o_goods <=
1; end
-
else begin o_money <=
0; o_goods <=
0; end
-
end
-
S2:
-
begin
-
if(i_dat==
5) begin o_money <=
2; o_goods <=
1; end
-
else begin o_money <=
0; o_goods <=
0; end
-
end
-
S3:
-
begin
-
if(i_dat==
5) begin o_money <=
3; o_goods <=
1; end
-
else
if (i_dat==
2) begin o_money <=
0; o_goods <=
1; end
-
else begin o_money <=
0; o_goods <=
0; end
-
end
-
S4:
-
begin
-
if(i_dat==
5) begin o_money <=
4; o_goods <=
1; end
-
else
if (i_dat==
2) begin o_money <=
1; o_goods <=
1; end
-
else
if (i_dat==
1) begin o_money <=
0; o_goods <=
1; end
-
end
-
default :
-
begin o_money <=
0; o_goods <=
0;end
-
endcase
-
end
-
-
/***************************不采用状态机的方法**********************************/
-
reg [
3:
0] add_in;
//内部计算输入的钱的和
-
reg [
3:
0] a_money;
//找零
-
reg a_goods;
//商品
-
always @(posedge clk
or posedge asy_rst)
-
begin
-
if(asy_rst)
-
begin
-
add_in <=
0;
-
a_money <=
0;
-
a_goods <=
0;
-
end
-
else
if(i_val)
-
begin
-
if(add_in+i_dat >=
5)
-
begin
-
add_in <=
0;
//清零
-
a_money <= add_in + i_dat -
5;
//找零
-
a_goods <=
1;
//输出
-
end
-
else
-
begin
-
add_in <= add_in + i_dat;
-
a_money <=
0;
-
a_goods <=
0;
-
end
-
end
-
else
-
begin
-
add_in <=
0;
-
a_money <=
0;
-
a_goods <=
0;
-
end
-
end
-
endmodule
-
/////////////////////////////////////testbench//////////////////////////////////
-
module auto_sell_sim;
-
parameter CLK_PERIOD =
10;
//仿真周期10ns=100M
-
parameter RST_CYCLE =
5;
//复位周期数
-
parameter RST_TIME = RST_CYCLE * CLK_PERIOD;
-
-
reg sim_clk;
-
reg sim_asy_rst;
-
-
initial
-
begin
-
sim_clk =
0;
-
sim_asy_rst =
1;
-
#RST_TIME sim_asy_rst = 0;
-
end
-
-
always
#(CLK_PERIOD/2) sim_clk = ~sim_clk;
-
/******************************************************/
-
parameter I_WD =
4;
-
parameter O_WD =
4;
-
reg [I_WD
-1:
0]sim_dat;
-
reg [I_WD
-1:
0]sim_i_dat;
-
reg sim_i_val;
-
always@(posedge sim_clk
or posedge sim_asy_rst)
-
begin
-
if(sim_asy_rst)
-
begin
-
sim_i_dat <=
0;
-
sim_i_val <=
0;
-
end
-
else
-
begin
-
sim_dat <= {$random}%
6;
//产生0-5的随机数
-
if( sim_dat==
1 || sim_dat ==
2 || sim_dat==
5)
-
begin
-
sim_i_dat <= sim_dat;
-
sim_i_val <=
1;
-
end
-
else
-
begin
-
sim_i_dat <=
0;
-
sim_i_val <=
1;
-
end
-
end
-
end
-
-
wire [O_WD
-1:
0] o_money;
-
wire o_goods;
-
auto_sell_better auto_sell_better_u(
//auto_sell auto_sell_u (
-
.clk( sim_clk ),
-
.asy_rst( sim_asy_rst ),
-
.i_dat( sim_i_dat ),
-
.i_val( sim_i_val),
-
.o_money(o_money),
-
.o_goods(o_goods)
-
);
2.2序列检测器
笔试题目:如果序列长度为8,需要8个状态,最少(3)个寄存器进行状态转换(mealy)。
题目:用状态机实现 101101 的序列检测。
思路:
画出mealy状态转换图,并进行化简[1],灰色表示合并为一个:
如果采用moore状态机,其状态就多了,且输出比mealy延时一个时钟参考[1]:
-
//mealy状态机
-
module seq_detect#(
-
parameter
I_WD =
1 ,
O_WD =
1
-
)(
-
input clk,
-
input asy_rst,
-
input [
I_WD-
1:
0] i_dat,
-
input i_val,
-
output reg [
O_WD-
1:
0] o_detect
-
);
-
-
parameter
S0=
6'b000001;
-
parameter
S1=
6'b000010;
-
parameter
S2=
6'b000100;
-
parameter
S3=
6'b001000;
-
parameter
S4=
6'b010000;
-
parameter
S5=
6'b100000;
-
-
//101101
-
reg [
5:
0] curr_s;
-
reg [
5:
0] next_s;
-
//每个时钟只产生一次状态变化
-
always @(posedge clk or posedge asy_rst)
-
begin
-
if(asy_rst)
-
curr_s <=
S0;
-
else
-
curr_s <= next_s;
-
end
-
-
//产生的下一状态的组合逻辑
-
always @(*)
-
begin
-
next_s = 'dx;
-
case(curr_s)
-
S0:
if(i_dat ==
1) next_s =
S1;
-
else next_s =
S0;
-
S1:
if(i_dat ==
0) next_s =
S2;
-
else next_s =
S1;
-
S2:
if(i_dat ==
1) next_s =
S3;
-
else next_s =
S0;
-
S3:
if(i_dat ==
1) next_s =
S4;
-
else next_s =
S2;
////// 1010---提取10
-
S4:
if(i_dat ==
0) next_s =
S5;
-
else next_s =
S1;
-
S5:
if(i_dat ==
1) next_s =
S0;
-
else next_s =
S0;
-
default: next_s =
S0;
-
endcase
-
end
-
/**************************时序逻辑的输出*******************************/
-
-
always @(posedge clk or posedge asy_rst)
-
begin
-
if(asy_rst)
-
begin
-
o_detect <=
0;
-
end
-
else
-
case(curr_s)
-
S0,
S1,
S2,
S3,
S4:
-
begin
-
o_detect <=
0;
-
end
-
S5:
-
begin
-
if(i_dat ==
1 ) o_detect <=
1;
-
end
-
default :
-
begin o_detect <=
0; end
-
endcase
-
end
-
endmodule
-
/////////////////////////////////////testbench/////////////////////////////////////
-
module seq_detect_sim;
-
parameter
CLK_PERIOD =
10;
//仿真周期10ns=100M
-
parameter
RST_CYCLE =
5;
//复位周期数
-
parameter
RST_TIME =
RST_CYCLE *
CLK_PERIOD;
-
-
reg sim_clk;
-
reg sim_asy_rst;
-
-
initial
-
begin
-
sim_clk =
0;
-
sim_asy_rst =
1;
-
#
RST_TIME sim_asy_rst =
0;
-
end
-
-
always #(
CLK_PERIOD/
2) sim_clk = ~sim_clk;
-
/******************************************************/
-
parameter
I_WD =
1;
-
parameter
O_WD =
1;
-
parameter
IN_SEQ =
32'b00101101_01011000_10110111_01010101;
-
-
reg [
4:
0]cnt;
-
reg sim_i_val;
-
always @(posedge sim_clk or posedge sim_asy_rst)
-
begin
-
if(sim_asy_rst)
-
begin
-
cnt <=
0;
-
sim_i_val <=
0;
-
end
-
else
-
begin
-
cnt <= cnt +
1;
-
sim_i_val <=
1;
-
end
-
end
-
-
wire [
O_WD-
1:
0] o_detect;
-
seq_detect seq_detect_u (
-
.clk(sim_clk ),
-
.asy_rst(sim_asy_rst ),
-
.i_dat(
IN_SEQ[cnt] ),
-
.i_val(sim_i_val),
-
.o_detect(o_detect)
-
);
-
endmodule
目录