【基础知识】~ 序列检测

0. 前言

序列检测是面试中的一个高频考点,必须要掌握。要求如下:

  1. 对与序列检测的原理能说清。
  2. 要会手撕代码(有两种方法能实现:状态机 和 移位寄存器),至少得掌握状态机的方法吧!!!

1. 什么是序列检测

对于一个输入为x,输出为y的系统,我们想要检测某一个序列是否是我们想要的序列,如果是,输出y为1(或0);若不是,输出y为0(或1)。
下边举个例子:

比如我们想要检测的序列为1001
输入 x001001
输出 y000001

这就叫做序列检测!!!

1.1 什么是可重叠序列检测?什么是不可重叠序列检测?

1.1.1 可重叠序列检测

直接举例说明即可===>

比如我们想要检测的序列为1001
输入 x001001101001001
输出 y000001000001001

此时我们的输出最后一位是1,因为输入序列的倒数第四位是上一个已经检测出来序列的末尾,也就是下一个序列的开头用的是上一个序列的结尾,这就是可重叠的。

1.1.2 不可重叠序列检测

同样举例来说明===>

比如我们想要检测的序列为1001
输入 x001001101001001
输出 y000001000001000

此时我们的输出最后一位是0,因为输入序列的倒数第三位是一个新序列的开头,也就是下一个序列的开头用的不是上一个序列的结尾,这就是不可重叠的。

2. 序列检测的方法

序列检测目前我知道的有2种,状态图法 和 移位寄存器法。

下边我以实现检测序列为1001的可重叠检测 为例。

2.1 状态图法

在这里插入图片描述
上图是原始状态转移图,具体步骤如下:
首先说明一下,图中的ABCD表示的是状态,线条上边表示的是:输入/输出。

  1. 先对序列的第一位取反,表示一直等待有效状态来临。在图中序列第一位是1,取反为0,在A出A入的部分体现;如果此时等到的是序列第一位,表示可以进入下一个状态,图中A出B入的部分。每个状态都有两种可能,此时,A状态完成。
  2. 来到B状态,此时B的状态为1。如果此时来一个0,此时为10,刚好是我们序列1001想要的,那直接进入下一个状态;如果此时来的是1,此时为11,很遗憾,不是我们想要的,那只能报废这个1之前的那个1了,重新回到1状态,即回到B本身。
  3. 来到C状态,此时C的状态为10。如果此时来了0,此时为100,刚好是序列1001想要的,直接进入下一个状态;如果来的是1,此时为101,哎,不想要,咋办?作废1前边的10,回到状态1继续,即回到B。
  4. 来到D状态,此时D的状态为100。如果此时来了0,此时为1000,不是序列1001想要的,咋办?只能全部报废,重来,也就是回到A;如果来的是1,此时为1001,刚好是我们的序列,那你说该咋办?回到A?那肯定不行啊!题目说了可重叠,那末尾的这个1可以作为下一个序列的开头,那只能回到B开始喽。此时的输出为1。完事!!!

注意:不可重叠的我就不写了,其实就是最后一步有点区别。

2.1.1 牛刀小试

2.1.1.1 题目一

VL11 状态机-非重叠的序列检测

2.1.1.1.1 状态图

在这里插入图片描述

2.1.1.1.2 代码实现
`timescale 1ns/1ns

module sequence_test1(
	input wire clk  ,
	input wire rst  ,
	input wire data ,
	output reg flag
);
//*************code***********//
    parameter IDLE = 6'b000000;
    parameter s0 = 6'b000010;
    parameter s1 = 6'b000100;
    parameter s2 = 6'b001000;
    parameter s3 = 6'b010000;
    parameter s4 = 6'b100000;
    
    
    reg [5:0] cur_state;
    reg [5:0] nex_state;
    //第一段:时序逻辑描述 状态转移
    always @ (posedge clk or negedge rst) begin
        if(!rst) begin
            cur_state <= IDLE;
        end
        else begin
           cur_state <= nex_state; 
        end
    end
    //第二段:组合逻辑描述 状态转移条件和规律
    always @ (*) begin
        case(cur_state)
            IDLE : nex_state = (data == 1'b1) ? s0 : IDLE;
            s0 : nex_state = (data == 1'b1) ? s0 : s1;
            s1 : nex_state = (data == 1'b1) ? s2 : IDLE;
            s2 : nex_state = (data == 1'b1) ? s3 : s1;
            s3 : nex_state = (data == 1'b1) ? s4 : s1;
            s4 : nex_state = (data == 1'b1) ? s0 : IDLE;
        endcase
    end
    //第三段:时序逻辑或者组合逻辑 描述 输出状态
    always @ (posedge clk or negedge rst) begin
        if(!rst) begin
           flag <= 1'b0; 
        end
        else begin
            //可以根据 次态单独判断(摩尔型) 或者 根据 现态和输入一起判断(米粒型)
//             if(cur_state == s3 && data == 1'b1) begin
            if(nex_state == s4) begin
               flag <= 1'b1; 
            end
            else begin
               flag <= 1'b0; 
            end
        end
    end

//*************code***********//
endmodule
2.1.1.2 题目二

VL12 状态机-重叠序列检测

2.1.1.2.1 状态图

在这里插入图片描述

2.1.1.2.2 代码实现
`timescale 1ns/1ns

module sequence_test2(
	input wire clk  ,
	input wire rst  ,
	input wire data ,
	output reg flag
);
//*************code***********//
    parameter IDLE = 5'b00001;
    parameter s0 = 5'b00010;
    parameter s1 = 5'b00100;
    parameter s2 = 5'b01000;
    parameter s3 = 5'b10000;
    
    reg [4:0] cur_state;
    reg [4:0] nex_state;
    
    //第一步:时序逻辑描述状态转移
    always @ (posedge clk or negedge rst) begin
        if(!rst) begin
           cur_state <= 5'b0; 
        end
        else begin
           cur_state <= nex_state; 
        end
    end
    //第二部:组合逻辑描述状态转移条件和规律
    always @ (*) begin
        case(cur_state)
            IDLE : nex_state = (data == 1'b1) ? s0 : IDLE;
            s0 : nex_state = (data == 1'b1) ? s0 : s1;
            s1 : nex_state = (data == 1'b1) ? s2 : IDLE;
            s2 : nex_state = (data == 1'b1) ? s3 : s1;
            s3 : nex_state = (data == 1'b1) ? s0 : s1;
            default : nex_state = IDLE;
        endcase
    end
    //第三步:时序逻辑描述状态输出
    always @ (posedge clk or negedge rst) begin
        if(!rst) begin
           flag <= 1'b0; 
        end
        else begin
//             if(cur_state == s2 && data == 1'b1) begin
            if(cur_state == s3) begin
               flag <= 1'b1; 
            end
            else begin
               flag <= 1'b0; 
            end
        end
    end


//*************code***********//
endmodule

注意第二个题和第一个题在第三步的时候不太一样,由于题中给出的波形要延后一拍,所以得注意一下!!!

2.1.2 总结

  1. FSM包括摩尔型和米粒型,摩尔型比米粒型多了一个状态,理想状态下三段式最后一段===> 摩尔型表示方式为:cur_state <= 最后一个状态,输出拉高;米粒型表示方式为:cur_state <= 最后一个状态 && data ,输出拉高。(上边两个题很奇怪,只能说看题中具体给出的波形吧)!!!
  2. 可重叠和不可重叠序列检测用三段状态机写的时候,差距就在第二段状态转移条件和规律,其他的两段不变。

2.2 移位寄存器法

这里我们以检测序列10010为例。

2.2.1 思路介绍

移位寄存器法比较简单粗暴,直接使用一个5位的寄存器寄存每一个码流,并在下一个时钟周期对其移位,再与需要的结果(10010)进行对比,根据对比结果输出检测结果。

非重复检测:最少都是码流”10010——10010“,那么两次成功输出会间隔最少5个时钟。所以我们可以设计一个计数器,当成功检测时将其清零,其他时候递加1。

重复检测:实际上重复检测的时候直接输出即可,而非重复检测则需要根据计数器的值进行判断。每次成功输出时需要判断此时计数器的值,若非重复检测则计数器的值应大于等于4(0-4共5个时钟)。

2.2.2 代码实现

//检测序列“10010”,使用移位寄存器法
module seqdet_reg
#(
	parameter	REPEAT = 1'b1			//1--重复检测;0--非重复检测
)                                       
(                                       
	input 		x		,				//输入	
	input		clk		,               //时钟
	input		rst_n	,               //复位信号,低电平有效
	output 	reg	z			            //输出采用时序逻辑,避免毛刺
);

reg	[4:0]	z_reg;						//移位寄存器
reg	[9:0]	cnt;						//计数器,这里位宽设置大点,留点余量1024
reg			first_flag;					//第一次检测标志,拉高则表示已经检测到了第一个“10010”

//将输入向左移位寄存
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)
		z_reg <= 5'd0;	
	else
		z_reg <= {z_reg[3:0],x};		//向左移位
end
//对比结果进行输出
always @(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		z <= 1'b0;	
		first_flag <= 1'b0;		
		cnt <= 10'd0;	
	end
	else if(REPEAT)begin				//重复检测到序列
			first_flag <= 1'b0;		
			cnt <= 10'd0;	
		if(z_reg == 5'b10010)			//捕捉到10010
			z <= 1'b1;		
		else
			z <= 1'b0;	
	end
	else begin							//非重复检测到序列
		if(z_reg == 5'b10010)begin		//捕捉到10010
			if(!first_flag)begin		//是第一个“10010”
				z <= 1'b1;
				first_flag <= 1'b1;		//拉高标志信号
				cnt <= 10'd0;			//计数器清零
			end
			else begin					//不是第一个“10010”
				if(cnt >= 'd4)begin		//间隔5个时钟以上(0-4)
					z <= 1'b1;
					cnt <= 10'd0;		//计数器清零
					first_flag <= first_flag;		
				end
				else begin
					z <= 1'b0;
					cnt <= cnt + 1;		//计数器累加1	
					first_flag <= first_flag;		
				end
			end
		end
		else begin						//没有捕捉到10010
			z <= 1'b0;
			cnt <= cnt + 1;				//计数器累加1	
			first_flag <= first_flag;		
		end
	end	
end

endmodule

=============================================================================

参考文献

高校课堂视频
刷题up
对比的一篇文章

声明

本人所有系列的文章,仅供学习,不可商用,如有侵权,请告知,立删!!!

本人主要是记录学习过程,以供自己回头复习,再就是提供给后人参考,不喜勿喷!!!

如果觉得对你有用的话,记得收藏+评论!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值