题解 | #不重叠序列检测#
不重叠序列检测
http://www.nowcoder.com/practice/9f91a38c74164f8dbdc5f953edcc49cc
1、状态机解法:
参考这个https://blog.nowcoder.net/n/5e18f294df6642bebb39f5b6e47df908?f=comment
状态机画的很好不重叠序列检测使用了zero和fail两个状态就很合理。
1.1) 问题:cnt到底计数到5还是6啊?
第六个上升沿输出,所以应该计数到6
1.2) 计数到6后从1开始重新计数
是因为six跳转到one,且fail跳转到1,所以应该从1开始计数。合理,因为第二波序列到来时就是从1开始计数的才对。
1.3 正确的计数模块:计数到6后从1开始重新计数
//计数模块
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
cnt <= 3'd0;
end
else
cnt <= (cnt == 3'd6) ? 3'd0 : (cnt + 3'd1);
end
状态机正确代码:比较繁琐,可化简。
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
output reg match,
output reg not_match
);
parameter [2:0] zero = 3'd0, one= 3'd1, two= 3'd2, three= 3'd3, four= 3'd4, five= 3'd5, six= 3'd6, fail= 3'd7;
reg [2:0] state, next_state, cnt;
//时序模块,状态转移
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
state <= zero;
end
else begin
state <= next_state;
end
end
//计数模块
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
cnt <= 3'd0;
end
else
cnt <= (cnt == 3'd6) ? 3'd1 : (cnt + 3'd1);
end
//组合逻辑 状态转移模块
always @ (*) begin
if(!rst_n)
next_state = zero;
else begin
case(state)
zero: next_state <= data ? fail : one;
one: begin
if(data) //if else 太繁琐,应该使用? : 运算符。
next_state = two;
else
next_state = fail;
end
two: begin
if(data)
next_state = three;
else
next_state = fail;
end
three: begin
if(data)
next_state = four;
else
next_state = fail;
end
four: begin
if(data)
next_state = fail;
else
next_state = five;
end
five: begin
if(data)
next_state = fail;
else
next_state = six;
end
six: begin
if(data)
next_state = fail;
else
next_state = one;
end
fail: begin
if(~data && cnt == 3'd6)
next_state = one;//fail跳变到1
else
next_state = fail;
end
default: next_state = zero;
endcase
end
end
//输出模块
always @ (*) begin
if(!rst_n) begin
match = 1'b0;
not_match = 1'b0;
end
else begin
match = (cnt == 3'd6) && (state == six);
not_match = (cnt == 3'd6) && (state == fail);
end
end
endmodule
2 移位寄存器
2.1 首先探讨cnt计时cnt==5和cnt==6的区别
由这张图可以看出,首先flag_1是在cnt为2和4发生反转,所以,当cnt在第三个时钟上升沿递增为3的时候,在flag_1模块内还是使用cnt=2
2.2 不管状态机还是移位寄存器,如果计数到6才置位的话必须重新开始从1开始计数才可以正确.很合理,因为第二波序列到来时就是从1开始计数的才对。
//counter
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
cnt <= 3'd0;
end
else
cnt <= (cnt == 3'd6) ? 3'd1 : (cnt + 3'd1);
end
2.3 自己修改的移位寄存器解法;计数到6,match赋值用组合逻辑实现
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
output reg match,
output reg not_match
);
parameter t_seq = 6'b011100;
reg [5:0] seq;
reg [2:0] cnt;
//counter
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
cnt <= 3'd0;
end
else
cnt <= (cnt == 3'd6) ? 3'd1 : (cnt + 3'd1);
end
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
seq <= 6'd0;
end
else begin
seq <= {seq[4:0], data};
end
end
always @ (*) begin
if(!rst_n) begin
match <= 1'b0;
not_match <= 1'b0;
end
else begin
// match <= (cnt == 3'd5) && ({seq[4:0], data} == t_seq);
// not_match <= (cnt == 3'd5) && ({seq[4:0], data} != t_seq);
match <= (cnt == 3'd6) && (seq == t_seq);
not_match <= (cnt == 3'd6) && (seq != t_seq);
end
end
endmodule
2.4 如果要计数到5,那么match的赋值必须要在时序模块内,第六个上升沿到达时,match赋值模块内cnt还是以5来计算,此时最新的data还没有移位到seq中,需要手动拼接一下才可以
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
output reg match,
output reg not_match
);
parameter t_seq = 6'b011100;
reg [5:0] seq;
reg [2:0] cnt;
//counter
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
cnt <= 3'd0;
end
else
cnt <= (cnt == 3'd5) ? 3'd0 : (cnt + 3'd1);
end
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
seq <= 6'd0;
end
else begin
seq <= {seq[4:0], data};
end
end
always @ (posedge clk, negedge rst_n) begin
if(!rst_n) begin
match <= 1'b0;
not_match <= 1'b0;
end
else begin
match <= (cnt == 3'd5) && ({seq[4:0], data} == t_seq);
not_match <= (cnt == 3'd5) && ({seq[4:0], data} != t_seq);
// match <= (cnt == 3'd5) && (seq == t_seq);
// not_match <= (cnt == 3'd5) && (seq != t_seq);
end
end
endmodule
3 正确解法:还是移位寄存器
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input data,
output reg match,
output reg not_match
);
reg [5:0] seq;
reg [3:0] counter;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
seq <= 6'b0;
match <= 1'b0;
not_match <= 1'b0;
end
else
seq <= { seq[4:0], data };
end
always@(posedge clk) begin
if(!rst_n) begin
counter <= 0;
end
else if(counter=='d5)
counter <= 0;
else begin
counter <= counter + 1;
end
end
always@(posedge clk)begin
if( {seq[4:0],data}==6'b011100 && counter=='d5 ) begin
match <= 1;
end
else begin
match <= 0;
end
end
always@(posedge clk)begin
if( {seq[4:0],data}!=6'b011100 && counter=='d5 ) begin
not_match <= 1;
end
else begin
not_match <= 0;
end
end
endmodule
总结:如果match使用时序模块进行赋值,则cnt计数到5即可,如果match使用组合逻辑赋值,则需要计数到6。 是否可以这样总结:对于序列检测,如果match使用时序模块进行赋值,则cnt计数到(序列长度-1)即可,如果match使用组合逻辑赋值,则需要计数到(序列长度)