序列检测器可以用状态机实现,可以是摩尔(输出只与当前的状态有关),也可以是米利(输出不仅与当前状态有关,还和当前输入有关)。看到一篇帖子是通过移位寄存器实现的,所以就自己试了试,记录一下。
相比于状态机实现,移位寄存器的思想很简单,就是比较,不停的比较,但是会带来一些功耗上的损失,与状态机实现的功耗相比如何,还需要进一步验证,但是确实可以节省面积。
核心思想: 设置一个移位寄存器,每个上升沿都要左移一次(一定是左移,不能是右移),然后与10010作比较。
-
对于数据比较算法:
- 如果是重复检测,对于10010010,这样会检测两次,这种最简单,直接比较就可以
- 如果是非重复检测,对于10010010,只输出一次的结果,可以使用计数器来计数,相邻输出之间至少要大于4,但是对于第一次的输出,可能无法满足计数为4,所以需要设置flag标志位。
明白了核心思想,代码和测试激励如下
seqdet.v
/*********************************************************************
* Author : liu
* Last modified : 2023-06-01 23:01
* Filename : seqdet.v
* Description :
* ******************************************************************/
module seqdet #(
parameter REPEAT = 1
)
(
input clk,
input rstn,
input data_in,
output reg z
);
// det 10010
reg first_flag;
reg [4:0] shift_reg;
reg [3:0] cnt;
always @(posedge clk or negedge rstn) begin
if(!rstn)
shift_reg <= 5'b0;
else
shift_reg <= {shift_reg[3:0], data_in};
end
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
first_flag <= 0;
cnt <= 4'b0;
end
else if(shift_reg == 5'b10010) begin
if(REPEAT) begin
z <= 1;
end
else if(!first_flag) begin
first_flag <= 1;
z <= 1;
cnt <= 4'b0;
end
else if(cnt >= 4'd4) begin
z <= 1;
cnt <= 4'b0;
end
end
else begin
z <= 0;
cnt <= cnt + 1;
end
end
endmodule
tb.v
module stimulus;
reg clk, rstn, data_in;
wire z;
initial begin
clk = 1;
rstn = 1;
#2 rstn = 0;
#2 rstn = 1;
end
initial begin
data_in = 0;
#5 data_in = 1;
#10 data_in = 1;
#10 data_in = 1;
#10 data_in = 0;
#10 data_in = 0;
#10 data_in = 1;
#10 data_in = 0;
#10 data_in = 0;
#10 data_in = 1;
#10 data_in = 0;
#10 data_in = 1;
#10 data_in = 1;
#10 data_in = 0;
#10 data_in = 0;
#10 data_in = 1;
#10 data_in = 0;
#20 $finish;
end
always #5 clk = !clk;
seqdet #(1)
u_seq_det (
.clk(clk),
.rstn(rstn),
.data_in(data_in),
.z(z)
);
initial begin
$fsdbDumpfile("testbench.fsdb");
$fsdbDumpvars;
end
endmodule