序列检测的Verilog设计。
一、概述
序列检测是手撕代码中经常会出现的一道题目。所谓序列检测就是将一个指定的序列从数字码流中识别出来,同时也可以实现对指定序列的计数。检测工具一般为状态机,通过状态机跳转来实现识别的过程。
状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。有限状态机(Finite State Machine)即FSM,可分为两种:
(1)输出只和状态有关,与输入无关,为Moore型状态机。
(2)输出和状态、输入都有关系,则称为Mealy型状态机。
实现序列检测也可以不使用状态机。状态机的跳转逻辑写起来会比较复杂,但是会比较直接,我们只需要关注每一步的跳转。不使用状态机的实现会更简洁,但是会使用更多的dff,尤其是当检测序列比较长的时候,会占用更多的面积,不利于低功耗的实现。
二、代码实现
(1)状态机
使用两种不同的状态机来实现,检测序列1101。并统计出现次数。
Moore型,输出只和状态有关:
module moore(
input clk, rst_n,
input din,
output dout,
output reg [31:0] cnt
);
reg [2:0] cs;
reg [2:0] ns;
parameter S0 = 3'b000; // 0
parameter S1 = 3'b001; // 1
parameter S2 = 3'b010; // 11
parameter S3 = 3'b011; // 110
parameter S4 = 3'b100; // 1101
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0)begin
cs <= S0;
end
else begin
cs <= ns;
end
end
always @(*) begin
case (cs)
S0: ns = (din==1)? S1:S0;
S1: ns = (din==1)? S2:S0;
S2: ns = (din==1)? S2:S3;
S3: ns = (din==1)? S4:S0;
S4: ns = (din==1)? S2:S0;
default: ns = S0;
endcase
end
assign dout = (cs == S4)? 1:0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 32'b0;
end
else if(cs == S4)begin
cnt <= cnt+1;
end
else begin
cnt <= cnt;
end
end
endmodule
测试波形:
Mealy型,输出和状态、输入都有关系:
module mealy(
input clk, rst_n,
input din,
output reg dout,
output reg [31:0] cnt
);
reg [1:0] cs;
reg [1:0] ns;
parameter S0 = 3'b00; // 0
parameter S1 = 3'b01; // 1
parameter S2 = 3'b10; // 11
parameter S3 = 3'b11; // 110
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0)begin
cs <= S0;
end
else begin
cs <= ns;
end
end
always @(*) begin
case (cs)
S0: ns = (din==1)? S1:S0;
S1: ns = (din==1)? S2:S0;
S2: ns = (din==1)? S2:S3;
S3: ns = (din==1)? S1:S0;
default: ns = S0;
endcase
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 0;
end
else if(cs == S3 && din == 1)begin
dout <= 1;
end
else begin
dout <= 0;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 32'b0;
end
else if(cs == S3 && din == 1)begin
cnt <= cnt+1;
end
else begin
cnt <= cnt;
end
end
endmodule
测试波形:
随着检测序列的加长,状态会越来越多,状态机会越来越复杂。如果是很长的一段序列,用状态机来实现就会非常困难。
(2)参数化实现
使用参数化实现来检测序列,主体是移位寄存器。
代码实现:
module para #(parameter SEQ = 8'b1101_1101,parameter WIDTH = 8)(
input clk,
input rst_n,
input din,
output dout,
output reg [31:0] cnt
);
reg [WIDTH-1:0] in_buf;
always @ (posedge clk or negedge rst_n)begin
if(!rst_n)begin
in_buf <= ~{WIDTH{SEQ[WIDTH-1]}};
end
else begin
in_buf <= {in_buf[WIDTH-2:0],din};
end
end
assign dout = (in_buf == SEQ)? 1:0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 32'b0;
end
else if(in_buf == SEQ)begin
cnt <= cnt+1;
end
else begin
cnt <= cnt;
end
end
endmodule
测试波形,序列1101_1101:
需要注意in_buf定义的复位值,不能简单为0,需要根据所检测的序列来判断。例如极端情况,检测序列为0000,如果复位值也都是0,此时检测会出现错误,所以需要复位为全1。简单来说就是要判断检测序列的最高位,并相应展宽取反。
测试波形,序列0000:
in_buf的复位值为F,为了检测全0的序列。