序列检测总结
文章目录
基础知识补充
状态分类
摩尔状态机(Moore):输出只与当前的状态有关。输入对输出的影响要到下一个时钟周期才能反映出来;
米利状态机(Mealy):输出不仅与当前状态有关,还和当前输入有关。输出是在输入信号变化以后立刻发生变化,且输入变化可能出现在任何状态的时钟周期内,常用三段式状态机。
状态机参考链接
三段式状态机
(0) 首先,根据状态机的个数确定状态机编码。利用编码给状态寄存器赋值,代码可读性更好。
(1) 状态机第一段,时序逻辑,非阻塞赋值(<=),传递寄存器的状态。
(2) 状态机第二段,组合逻辑,阻塞赋值(=),根据当前状态和当前输入,确定下一个状态机的状态。
(3) 状态机第三代,时序逻辑,非阻塞赋值(<=),因为是 Mealy 型状态机,根据当前状态和当前输入,确定输出信号。
序列检测分类
重复检测:一串序列1011011需要检测出1011的个数,第一个的1011和第二个的1011有一个共用的“1”,这就是重复检测。
非重复检测:反之当检测序列1011011中1011的个数时只会检测到有一个1011,而不是两个。
重复序列检测
状态机实现重复序列检测
代码如下:
module Squence_detect_rep(
input clk,reset,dataIn,
output detect_flag
);
parameter s_idle = 5'b0_0001;
parameter s1 = 5'b0_0010;
parameter s2 = 5'b0_0100;
parameter s3 = 5'b0_1000;
parameter s4 = 5'b1_0000;
reg [4:0] c_s;
reg [4:0] n_s;
reg detect_flag_tep ;
// 第一段 时序逻辑,状态转移
always @(posedge clk or posedge reset)
if (reset)
c_s <= s_idle;
else
c_s <= n_s;
// 第二段 组合逻辑,确定n_s的状态
always @(*) begin
//是否在组合逻辑中不需要考虑reset
case(c_s)
s_idle : n_s = (dataIn==1'b1) ? s1 : s_idle;
s1 : n_s = (dataIn==1'b0) ? s2 : s1;
s2 : n_s = (dataIn==1'b1) ? s3 : s_idle;
s3 : n_s = (dataIn==1'b1) ? s4 : s2;
s4 : n_s = (dataIn==1'b1) ? s1 : s2;
endcase
end
// 第三段 时序逻辑,判断c_s状态和输出
always @(posedge clk or negedge reset) begin
if (reset)
detect_flag_tep <= 1'b0;
else if (n_s == s4)
detect_flag_tep <= 1'b1;
else
detect_flag_tep <= 1'b0;
end
assign detect_flag = detect_flag_tep; // 此处的赋值没有延时
endmodule
移位寄存器实现重复序列检测
代码如下:
module Squence_detect_rep_shift
(input clk,reset,dataIn,
output detect_flag
);
reg [3:0] shift_reg;
reg detect_flag_tep;
always @(posedge clk or negedge reset) begin
if (reset)
shift_reg <= 4'b0000;
else
shift_reg <= {shift_reg[2:0],dataIn};
end
always @(posedge clk or negedge reset) begin
if (reset)
detect_flag_tep <= 1'b0;
else if (shift_reg == 4'b1011)
detect_flag_tep <= 1'b1;
else
detect_flag_tep <= 1'b0; //不要忘了else!!!!!
end
assign detect_flag = detect_flag_tep;
endmodule
不重复序列检测
状态机实现不重复序列检测
代码如下:
module Squence_detect_no_rep
(
input clk,reset,dataIn,
output detect_flag
);
// 状态机实现
parameter s_idle= 5'b0_0001;
parameter s1 = 5'b0_0010;
parameter s2 = 5'b0_0100;
parameter s3 = 5'b0_1000;
parameter s4 = 5'b1_0000;
reg [4:0] c_s;
reg [4:0] n_s;
reg detect_flag_tep;
// 时序逻辑,更新状态
always @(posedge clk or posedge reset) begin
if (reset)
c_s <= s_idle;
else
c_s <= n_s;
end
// 组合逻辑,状态跳转
always @(*) begin
case(c_s)
s_idle : n_s = (dataIn == 1'b1) ? s1:s_idle;
s1 : n_s = (dataIn == 1'b0) ? s2:s1;
s2 : n_s = (dataIn == 1'b1) ? s3:s_idle;
s3 : n_s = (dataIn == 1'b1) ? s4:s2;
s4 : n_s = (dataIn == 1'b1) ? s1:s_idle;
endcase
end
// 时序逻辑,输出赋值
always @(posedge clk or negedge reset) begin
if (reset)
detect_flag_tep <= 1'b0;
else if (n_s == s4)
detect_flag_tep <= 1'b1;
else
detect_flag_tep <= 1'b0;
end
assign detect_flag = detect_flag_tep;
endmodule
寄存器实现不重复序列检测
代码如下:
module Squence_detect_no_shift
(
input clk,reset,dataIn,
output detect_flag
);
reg [3:0] shift_reg;
reg detect_flag_tep;
reg [2:0] cnt,cnt_max;
always @(posedge clk or negedge reset) begin
if (reset)
shift_reg <= 4'b0000;
else
shift_reg <= {shift_reg[2:0],dataIn};
end
always @(posedge clk or posedge reset) begin
if (reset)
cnt <= 3'd0;
else if (shift_reg == 4'b1011 && cnt==3'd0)
cnt <= 3'd4; // 当检测到一次后,需要移位寄存器左移四次,再进行下一次检测
else if (cnt>0)
cnt <= cnt - 1'd1;
else
cnt <= 3'd0;
end
always @(posedge clk or posedge reset) begin
if (reset)
detect_flag_tep <= 1'b0;
else if (shift_reg == 4'b1011 && cnt==3'd0)
detect_flag_tep <= 1'b1;
else
detect_flag_tep <= 1'b0;
end
assign detect_flag = detect_flag_tep;
endmodule
testbench 生成
代码如下:
module Squence_detect_tb();
reg clk,reset,dataIn;
wire flag_out;
wire flag_out_repeat_shift;
wire flag_out_no_repeat;
wire flag_out_no_repeat_shift;
integer clk_period_half = 10;
// generate clk
always begin
clk = 1'b0;
# clk_period_half;
clk = 1'b1;
# clk_period_half;
end
// generate reset and data
initial begin
reset = 1'b0;
dataIn = 1'b0;
# (clk_period_half * 10); // 出现某些中间运算时,需要括号
reset = 1'b1;
dataIn = 1'b0;
# clk_period_half;
reset = 1'b0;
dataIn = 1'b0;
#20 dataIn=1;
#20 dataIn=0;
#20 dataIn=1;
#20 dataIn=0;
#20 dataIn=1;
#20 dataIn=1;
#20 dataIn=0;
#20 dataIn=1;
#20 dataIn=1;
#20 dataIn=0;
#20 dataIn=1;
#20 dataIn=1;
end
// 例化 instantiate
Squence_detect_rep u_Squence_detect_rep
(
.clk (clk),
.reset (reset),
.dataIn(dataIn),
.detect_flag(flag_out)
);
Squence_detect_rep_shift u_Squence_detect_rep_shift
(
.clk (clk),
.reset (reset),
.dataIn(dataIn),
.detect_flag(flag_out_repeat_shift)
);
Squence_detect_no_rep u_Squence_detect_no_rep
(
.clk (clk),
.reset (reset),
.dataIn(dataIn),
.detect_flag(flag_out_no_repeat)
);
Squence_detect_no_rep_shift u_Squence_detect_no_rep_shift
(
.clk (clk),
.reset (reset),
.dataIn(dataIn),
.detect_flag(flag_out_no_repeat_shift )
);
endmodule
仿真总结
- 如果是可重叠序列检测,移位寄存器的方式代码更加简洁,面积也更小。
- 如果是不可重叠序列检测,三段式状态机的实现方式思路更加清晰,不易出错。
- 对于不可重叠序列检测,如果采用移位寄存器的方式,要保证每检测到一次序列后,都需要将移位寄存器左移四位以后,再进行下一次的检测和判断。(需要不断更新计数器)