记录一些学习心得与经典电路,欢迎大佬批评指正不吝赐教
本篇记录分别采用两段式和三段式状态机的写法,用米里型和摩尔型状态机实现序列检测功能
状态机结构图
两段式状态机结构图
三段式状态机结构图
本文要检测的序列是1101,并且可以实现重复检测,因此要注意最终状态之后不一定回到初始状态。
摩尔型状态机
摩尔型状态机与输入无关,只与当前状态有关
两段式状态机
状态转换图如图所示:
代码如下:
`timescale 1ns / 1ps
module seq(
input clk,
input rst_n,
input din,
output reg sign
);
reg [4:0] state;
reg [4:0] state_n;
parameter
IDLE = 5'b00001,
s0 = 5'b00010,
s1 = 5'b00100,
s2 = 5'b01000,
s3 = 5'b10000;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
state <= IDLE;
else
state <= state_n;
end
always@(*)
begin
case(state)
IDLE:begin
if(din == 1'b1) state_n = s0;
else state_n = IDLE;
end
s0: begin
if(din == 1'b1) state_n = s1;
else state_n = IDLE;
end
s1: begin
if(din == 1'b0) state_n = s2;
else state_n = s1;
end
s2: begin
if(din == 1'b1) state_n = s3;
else state_n = IDLE;
end
s3: begin
if(din == 1'b1) state_n = s1;
else state_n = IDLE;
end
default: begin
state_n = IDLE;
end
endcase
end
always@(*)
begin
if(!rst_n)
sign = 1'b0;
else
begin
if(state == s3)
sign = 1'b1;
else
sign = 1'b0;
end
end
endmodule
testbench如下:采用了循环的方式重复赋值
`timescale 1ns / 1ps
module tb();
reg clk;
reg rst_n;
wire din;
wire sign;
reg[17:0] data;
initial begin
clk = 0;
rst_n =0;
#40
rst_n = 1;
#3000
$stop;
end
always #5 clk = ~clk;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
data <= 18'b1101_1011_1001_1011_00;
else
data <= {data[16:0],data[17]};
end
assign din = data[17];
seq seq1(
.clk(clk),
.rst_n(rst_n),
.din(din),
.sign(sign)
);
endmodule
仿真结果如图所示,可以发现当state处于s3状态时,输出sign信号为持续一个周期的高电平,并且第一个sign高电平与第二个sign高电平之间实现了连续检测。此时的sign输出高电平是在1101这个待检测序列已经传递完之后才有效的,注意与下面的米里型进行区分,米里型的sign信号是在收到第四个数字为1的同时就输出为1的。
三段式状态机
状态转换图同上
代码如下:
`timescale 1ns / 1ps
module seq(
input clk,
input rst_n,
input din,
output reg sign
);
reg [4:0] state;
reg [4:0] state_n;
parameter
IDLE = 5'b00001,
s0 = 5'b00010,
s1 = 5'b00100,
s2 = 5'b01000,
s3 = 5'b10000;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
state <= IDLE;
else
state <= state_n;
end
always@(*)
begin
case(state)
IDLE:begin
if(din == 1'b1) state_n = s0;
else state_n = IDLE;
end
s0: begin
if(din == 1'b1) state_n = s1;
else state_n = IDLE;
end
s1: begin
if(din == 1'b0) state_n = s2;
else state_n = s1;
end
s2: begin
if(din == 1'b1) state_n = s3;
else state_n = IDLE;
end
s3: begin
if(din == 1'b1) state_n = s1;
else state_n = IDLE;
end
default: begin
state_n = IDLE;
end
endcase
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
sign <= 1'b0;
else
begin
if(state_n == s3)
sign <= 1'b1;
else
sign <= 1'b0;
end
end
endmodule
testbench同上
仿真结果如下:
同样可以发现,当状态为s3时候,输出sign为持续一个周期的高电平。
米里型状态机
米里型状态机不仅与当前状态有关,也与输入信号有关
两段式状态机
状态转移图如图所示:
代码如下:
module seq(
input clk,
input rst_n,
input din,
output reg sign
);
reg [3:0] state;
reg [3:0] state_n;
parameter
s0 = 4'b0001,
s1 = 4'b0010,
s2 = 4'b0100,
s3 = 4'b1000;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
state <= s0;
else
state <= state_n;
end
always@(*)
begin
case(state)
s0: begin
if(din == 1'b1) state_n = s1;
else state_n = s0;
end
s1: begin
if(din == 1'b1) state_n = s2;
else state_n = s0;
end
s2: begin
if(din == 1'b0) state_n = s3;
else state_n = s2;
end
s3: begin
if(din == 1'b1) state_n = s1;
else state_n = s0;
end
default: begin
state_n = s0;
end
endcase
end
always@(*)
begin
if(!rst_n)
sign = 1'b0;
else
begin
if((state == s3) && (din == 1'b1))
sign = 1'b1;
else
sign = 1'b0;
end
end
endmodule
testbench和摩尔型的一样
仿真结果如图所示:
三段式状态机
代码如下:
module seq(
input clk,
input rst_n,
input din,
output reg sign
);
reg [3:0] state;
reg [3:0] state_n;
parameter
s0 = 4'b0001,
s1 = 4'b0010,
s2 = 4'b0100,
s3 = 4'b1000;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
state <= s0;
else
state <= state_n;
end
always@(*)
begin
case(state)
s0: begin
if(din == 1'b1) state_n = s1;
else state_n = s0;
end
s1: begin
if(din == 1'b1) state_n = s2;
else state_n = s0;
end
s2: begin
if(din == 1'b0) state_n = s3;
else state_n = s2;
end
s3: begin
if(din == 1'b1) state_n = s1;
else state_n = s0;
end
default: begin
state_n = s0;
end
endcase
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
sign <= 1'b0;
else
begin
if((state_n == s3) && (din == 1'b1))
sign <= 1'b1;
else
sign <= 1'b0;
end
end
endmodule
此时本来用的依然是上面的testbench,但是仿真后发现sign输出始终为0,经过改正testbench,发现如果采用下面的这种写法或者一位一位激励din都不会有问题,猜测可能是因为原先的这种写法是在时钟上升沿来临时才赋值给对data进行操作,然后再对din进行操作,编译过程中可能会导致在时钟上升沿处判断错误,如果有大佬有不同的想法,欢迎不吝赐教批评指正,万分感谢。
testbench写法:
`timescale 1ns / 1ps
module tb();
reg clk;
reg rst_n;
reg din;
wire sign;
integer i;
reg[17:0] data;
initial begin
clk = 0;
rst_n = 0;
data = 18'b1101_1011_1001_1011_00;
din = 0;
#10
rst_n = 1;
#5
for(i = 17 ; i >= 0 ; i = i-1)
begin
#10
din = data[i];
end
#3000
$stop;
end
always #5 clk = ~clk;
seq seq1(
.clk(clk),
.rst_n(rst_n),
.din(din),
.sign(sign)
);
endmodule
仿真结果如图所示:
米里型状态机的检测信号输出的位置是在最后一个1输入的同时拉高的,与摩尔型不同。
如果想实现单独检测的功能,将最后一个状态都指回到最初的状态即可。