Verilog4——状态机的原理、结构与设计以及序列检测功能实现

Verilog4——状态机的原理、结构与设计

一、知识讲解:

为什么要使用状态机?

在RTL级的Verilog语言描述当中,通过时序逻辑抽象所得到的有限状态机是其描述的依据。因此,把时序逻辑抽象转换成一个同步有限状态机是设计可综合风格的VerilogHDL模块的关键。(而对于组合逻辑,掌握可综合的组合逻辑设计实例即可。)

有限状态机:

在有限个状态之间按一定规律转换的时序电路。

时钟同步的状态机结构——Mealy&Moore:

  • **Mealy:**Mealy 型状态机的输出,不仅与当前状态有关,还取决于当前的输入信号。

Mealy 型状态机的输出是在输入信号变化以后立刻发生变化,且输入变化可能出现在任何状态的时钟周期内。因此,同种逻辑下,Mealy 型状态机输出对输入的响应会比 Moore 型状态机早一个时钟周期。

xpIhKU.png

  • **Moore :**Moore 型状态机的输出只与当前状态有关,与当前输入无关。

输出会在一个完整的时钟周期内保持稳定,即使此时输入信号有变化,输出也不会变化。输入对输出的影响要到下一个时钟周期才能(通过输入改变——状态改变——输出改变的流程)反映出来。

这也是 Moore 型状态机的一个重要特点:输入与输出是隔离开来的。

xpI4rF.png

有限状态机设计步骤:

1-逻辑抽象得到状态转移图:把实际问题中的逻辑转换关系(比如交通灯转换、序列检测中的位数变化、自动售货机的投币和输出等等)表示成时序逻辑函数,表达形式可以是状态转换表或状态转换图。

​ 1,分析问题,确定输入输出变量(接口定义)、电路状态数目(状态定义);

​ 2,定义输入输出逻辑状态含意,为状态顺序编号;

​ 3,画出转换图/表;

2-状态化简:合并等价状态,得到最简转换。

3-状态分配编码:选定编码方式(多选用独热码)为各个状态赋编码定义。【独热码每次每个状态中只有一个寄存器置位,可以既保障电路性能又充分发挥 触发器数量多的优势。】

4-选定触发器类型,求状态转移方程和输出方程。

5-描述逻辑转换图:使用VerilogHDL抽象建模语句,always和case等语句以及赋值语句即可实现。

可综合状态机语言指导原则:

1-建议采用独热码为状态进行编码;状态必须明确赋值。通常使用参数parameter或宏定义define语句加上赋值语句来实现

//parameter
parameter IDLE=5'b00000,
		 START=5'b00001;
current = START;

//define
`define IDLE 5'b00000
`define START 5'b00001

current = `START;

2-建议采用case,casex,casez语句来建立状态机的模型;

3-always块中,

//每个always块中只能有一个事件控制always@(event-expression)
always@(posedge clk or negedge rst_n)
//带有posedge或negedge关键字的事件表达式表示边沿触发的时序逻辑,没有这两个关键字的表示组合逻辑或者电平敏感的触发器。
always@(posedge clk or  valid)
//每个表示时序逻辑的always块只能由一个时钟跳变沿触发,置位或复位最好也由这个时钟边沿触发。
//每个always块中的赋值信号都必须定义为reg型或integer型
//每个纯表示组合逻辑的always块中,参与赋值的所有信号都要有明确的值,即赋值表达式右端的信号都必须在always@(敏感电平列表)中列出。(未列出但参与赋值的变量会被放在一个透明锁存器中暂存起来)
    always@(a or b or c)begin
        e = d & a & b;//d的变化不会立马引起e的变化,要等待ab变化后再执行计算
    end
//对一个reg型或integ型变量赋值,只能在一个always块中进行。

典型三段式状态机:

`timescale 1ns/1ns
//**输入输出接口**//
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
//**状态编码、参数定义**//
    parameter IDLE=0,ONE=1,TWO = 2,THREE = 3,FOUR= 4,FIVE= 5,SIX= 6,SEVEN= 7,EIGHT= 8;

    reg [7:0]cur_state, nxt_state;
    
//**第一段:时序逻辑,次态赋值给现态**//
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cur_state <= IDLE;
        end
        else begin
            cur_state <= nxt_state;
        end
    end
    
 //**第二段:组合逻辑,描述现态和输入到输出之间的逻辑转换**//
    always@(*)begin
        case(cur_state)
            IDLE: nxt_state = (a==0) ?ONE :IDLE;//必须在第一个要输入一个0,才能启动,不能把复位的0当成初始状态
            ONE: nxt_state = (a==0) ?ONE :TWO;
            TWO: nxt_state = (a==0) ?ONE :THREE;
            THREE: nxt_state = (a==0) ?ONE :FOUR;
            FOUR: nxt_state = (a==0) ?FIVE :IDLE;
            FIVE: nxt_state = (a==0) ?SIX :TWO;
            SIX: nxt_state = (a==0) ?SEVEN :TWO;
            SEVEN: nxt_state = (a==0) ?ONE :EIGHT;
            EIGHT: nxt_state = (a==0) ?ONE :THREE;
            default: nxt_state = IDLE;
        endcase
    end
 //**第三段:同步时序逻辑,描述状态输出**//
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            match <= 0;
        end
        else if (cur_state == EIGHT)begin
            match <= 1;
        end
        else
             match <= 0;
    end
endmodule

二、实例应用:

状态转移表实现时序电路

题目描述:

xpIR2V.png

思路分析:

要求使用D触发器实现,可通过状态转移表得到输出的逻辑表达式.由题目可以看出,输出Y是与输入A和当前状态Q1nQ0n都有关系。

基础的时序电路设计,可采用列激励方程、输出方程,进而用D触发器和组合逻辑电路实现的方案。

由状态表可得出,电路共4个状态,所以使用2个寄存器来实现状态的寄存。两个寄存器的输出为Q1和Q0。

可以得到次态Q1(n+1)、Q0(n+1)、输出Y的逻辑表达式为:

Q1n+1=D1=A⊕Q1n⊕Q0n

Q0n+1=D0=Q0n

A=Q0n*Q1n

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

module seq_circuit(
      input                A   ,
      input                clk ,
      input                rst_n,
 
      output   wire        Y   
);
    reg q0, q1;
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            q1 <= 0;
        end
        else begin
            q1 <= A ^ q0 ^ q1;
        end
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            q0 <= 0;
        end
        else begin
            q0 <= ~q0;
        end
    end
    assign Y = q0 & q1;
endmodule

方法二:状态机实现

//两种方法:
//一是写表达式用D触发器来描述
//二是写状态机描述状态转移过程
`timescale 1ns/1ns

module seq_circuit(
      input                A   ,
      input                clk ,
      input                rst_n,
 
      output   wire        Y   
);
    reg[1:0]current_state,next_state;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            current_state <= 2'b00;
            next_state <= 2'b00;
        end
        else begin
              current_state<= next_state;
        end
    end
    
    always@(*)begin
        case(current_state)
            2'b00: begin
                next_state = (A == 0)? 2'b01 : 2'b11;

            end
            2'b01: begin
                next_state = (A == 0)? 2'b10 : 2'b00;
               
            end
            2'b10: begin
                next_state = (A == 0)? 2'b11 : 2'b01;
               
            end
            2'b11: begin
                next_state = (A == 0)? 2'b00 : 2'b10;
              
            end
            default: next_state = next_state;
        endcase
       
    end
     assign Y = (current_state == 2'b11)? 1: 0;
endmodule

输入序列连续的序列检测

题目描述:

思路分析:

1-有限状态机:输入信号在8个状态之间转移,由状态机描述即可。

2-移位寄存器:设置一个和序列等长的寄存器,每个时钟都将输入移入寄存器的最低位,并判断寄存器中的值是否与序列相同。

代码实现:
//方法一:有限状态机
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    //parameter IDLE=0,ONE='b0,TWO = 'b01,THREE = 'b011,FOUR= 'b0111,FIVE= 'b01110,SIX= 'b011100,SEVEN= 'b0111000,EIGHT= 'b01110001;
    parameter IDLE=0,ONE=1,TWO = 2,THREE = 3,FOUR= 4,FIVE= 5,SIX= 6,SEVEN= 7,EIGHT= 8;
    //parameter IDLE='b00000000,ONE='b00000000,TWO = 'b00000001,THREE = 'b00000010,FOUR= 'b00000100,FIVE= 'b00001000,SIX= 'b00010000,SEVEN= 'b00100000,EIGHT= 'b01000000;
    
    //不理解这里的编码有什么影响?
    reg [7:0]cur_state, nxt_state;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cur_state <= IDLE;
        end
        else begin
            cur_state <= nxt_state;
        end
    end
    always@(*)begin
        case(cur_state)
            IDLE: nxt_state = (a==0) ?ONE :IDLE;//必须在第一个要输入一个0,才能启动,不能把复位的0当成初始状态
            ONE: nxt_state = (a==0) ?ONE :TWO;
            TWO: nxt_state = (a==0) ?ONE :THREE;
            THREE: nxt_state = (a==0) ?ONE :FOUR;
            FOUR: nxt_state = (a==0) ?FIVE :IDLE;
            FIVE: nxt_state = (a==0) ?SIX :TWO;
            SIX: nxt_state = (a==0) ?SEVEN :TWO;
            SEVEN: nxt_state = (a==0) ?ONE :EIGHT;
            EIGHT: nxt_state = (a==0) ?ONE :THREE;
            default: nxt_state = IDLE;
        endcase
    end
  
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            match <= 0;
        end
        else if (cur_state == EIGHT)begin
            match <= 1;
        end
        else
             match <= 0;
    end
endmodule



//方法二:移位寄存器
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
);
  	reg[7:0] a_r;
	always@(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
        	a_r<='b0;
    	end
    	else begin
        	a_r<={a_r[6:0],a}; 
    	end
	end
  
	always@(posedge clk or negedge rst_n) begin
      if(~rst_n) begin
    	   match <= 1'b0;
  	    end
        else begin
           match <= a_r==8'b01110001;
        end
	end
endmodule

含有无关项的序列检测

题目描述

思路分析:

解法有两种:状态机法和序列缓存对比法。

状态机法的过程:在初始状态中,先判断第一位是否符合,若符合则进入下一个状态,判断第二位是否符合;若第一位不符合则保持在初始状态,直到第一位匹配。如前两位匹配,则判断第三位是否符合,若第一位匹配,最新输入的数值和目标序列的第二位不匹配,则根据最新一位是否匹配第一位,进入第一位匹配状态或者初始状态。依次类推。

而在本题中,有空闲状态IDLE和序列状态0,01,011,011x,011xx,011xxx,011xxx1,011xxx11,011xxx110,即S0-S8共10个状态,状态IDLE,S0,S1,S2,的跳转逻辑与前一题目的状态机相同,而中间无关项的状态S3,S4,S5会影响到后续S6,S7,S8的跳转,如若无关项S5时a的序列为011011,则a=0,S5跳往S2,a=1,S5跳往S6,而若S5时a的序列为011010则a=0,S5跳往S0,a=1,S5跳往S6,这里需要判断三个无关项之后的跳转去向。

序列缓存对比法,则是将九个时刻的数据缓存,作为一个数组,每个时刻的输入位于数组的末尾,数组其它元素左移,把最早输入的数据移出。然后截取数组的前三位和目标序列011对比,截取数组的后三位和目标序列110对比,如果两段数组都和目标序列相等,则说明出现目标序列。

代码实现:
`timescale 1ns/1ns
//序列检测法:本题应用序列检测相对更简单
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    reg[8:0]a_reg;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            a_reg <= 9'b0;
        else 
            a_reg <= {a_reg[7:0],a};
    end
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            match <= 0;
        end
        else begin
            match <= (a_reg[2:0] == 'b110) && (a_reg[8:6] == 'b011);
        end
    end
endmodule
//状态机法
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
parameter IDLE = 4'd0; 
parameter S0 = 4'd1; 
parameter S1 = 4'd2; 
parameter S2 = 4'd3; 
parameter S3 = 4'd4; 
parameter S4 = 4'd5; 
parameter S5 = 4'd6; 
parameter S6 = 4'd7; 
parameter S7 = 4'd8;
parameter S8 = 4'd9;
reg [3:0] seq_cs;
reg [3:0] seq_ns;
reg [2:0] xxx_cs;
reg [2:0] xxx_ns;
always@(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        seq_cs <= IDLE;
        xxx_cs <= 3'b000;
    end
    else begin
        seq_cs <= seq_ns;
        xxx_cs <= xxx_ns;
    end
end

always@(*) begin
    seq_ns = IDLE;
    xxx_ns = 3'b000;
    case(seq_cs)
        IDLE: begin
            if(a == 0) seq_ns = S0;
            else seq_ns = IDLE;
        end
        S0: begin
            if(a == 0) seq_ns = S0;
            else seq_ns = S1;
        end
        S1: begin
            if(a == 0) seq_ns = S0;
            else seq_ns = S2;
        end
        S2: begin
            seq_ns = S3;
            xxx_ns = {xxx_ns[1:0], a};
        end
        S3: begin
            seq_ns = S4;
            xxx_ns = {xxx_ns[1:0], a};
        end
        S4: begin
            seq_ns = S5;
            xxx_ns = {xxx_ns[1:0], a};
        end
        S5: begin
            if(a == 0 && xxx_cs == 3'b011) seq_ns = S2;
            else if(a == 0) seq_ns = S0;
            else seq_ns = S6;
        end
        S6: begin
            if(a == 0) seq_ns = S0;
            else seq_ns = S7;
        end
        S7: begin
            if(a == 0) seq_ns = S8;
            else seq_ns = IDLE;
        end
        S8: begin
            if(a == 0) seq_ns = S0;
            else seq_ns = S1;
        end
    endcase
end
always@(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        match <= 1'b0;
    end
    else begin
        match <= (seq_cs == S8);
    end
end
    endmodule

不重叠序列检测

题目描述

思路分析:

同样有两种方法:状态机法和移位寄存器法

状态机法:题目要求,一旦出现不匹配序列,则这一段的6位数跳过不再检测,且这一批次输入结束后给出不匹配信号,并等待下一批次的六位序列再开始检测,若序列完全符合则6位输入结束后输出匹配信号。那么状态可以设置为闲置状态:

IDLE,S0,S1,S2,S3,S4,S5,SF0,SF1,SF2,SF3,SF5,这种状态设计的转移图为

或者,状态设置为,ZERO,S0,S1,S2,S3,S4,S5,FAIL,将所有不符合的状态设置为FAIL,同时添加时钟计数,满6清零,在FAIL状态中, 只有当计数器计满方可跳入下一状态从而实现序列的批次检测。状态转移如下:

移位寄存器:同样的,寄存器中的数据只能在计数器计满时符合待检测序列才算满足条件

代码实现:
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input data,
	output reg match,
	output reg not_match
	);
    parameter IDLE = 0,A=1,B=2,C=3,D=4,E=5,F=6,FAIL=7;
    reg[2:0]cur_state,nxt_state,cnt;
    
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cur_state <= IDLE;
        else 
            cur_state <= nxt_state;
    end
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt <= 0;
        else 
            cnt <= (cnt==3'd6)? 1 : cnt+'d1;
    end
    always@(*)begin    //这里要写成组合逻辑形式的always
        if(!rst_n)
            nxt_state = IDLE;
        else
        case(cur_state)
            IDLE:nxt_state = (data == 0)?A:FAIL;
            A:nxt_state = (data == 0)? FAIL: B ;
            B:nxt_state = (data == 0)? FAIL: C ;
            C:nxt_state = (data == 0)? FAIL: D ;
            D:nxt_state = (data == 0)? E: FAIL ;
            E:nxt_state = (data == 0)? F: FAIL ;
            F:nxt_state = (data == 0)? A: FAIL ;
            FAIL:nxt_state = (data==0 && cnt==3'd6)?A:FAIL;
            default: nxt_state = IDLE;
        endcase
    end
    
    //match和not_match都是在检测完一段序列之后才会有判断是否拉高,没检测完整时不做判断
    always@(*)begin    //这里要写成组合逻辑形式的always
         if(!rst_n)begin
            match = 0;
            not_match = 0;
         end
         else if(cnt==3'd6) begin
             if(cur_state == F)begin
                match = 1;
                not_match = 0;
             end
             else if(cur_state == FAIL)begin
                 match = 0;
                not_match = 1;
             end
             else begin
                  match = 0;
                not_match = 0;
             end
         end
         else begin
                 match = 0;
                not_match = 0;
         end
        end

endmodule
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input data,
	output reg match,
	output reg not_match
	);
    
    reg [2:0] cnt;
    reg [5:0] data_r;
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            cnt <= 0;
        else 
            cnt <= cnt==5? 0: cnt+1;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            data_r <= 6'b0; 
        else
            data_r <= {data_r[4:0], data};
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            match     <= 1'b0;
            not_match <= 1'b0;
        end
        else begin
            match     <= (cnt==5) && ({data_r[4:0], data}==6'b011100);
            not_match <= (cnt==5) && ({data_r[4:0], data}!=6'b011100);
        end
    end
    
endmodule

输入序列不连续的序列检测:

题目描述:

思路分析:

状态机法:相当于状态机的跳转控制信号变成了两个,输入的序列数据data和使能信号data_valid,状态仍设为IDLE,S0,S1,S2,S3,状态转移图如下:

移位寄存器:寄存器中的序列值只有在使能信号data_valid有效时才可以更新(移位读入)

代码实现:
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input data,
	input data_valid,
	output reg match
	);
    parameter IDLE=4'd0,A=1,B=2,C=3,D=4;            
    reg[2:0]cur_state,nxt_state;
   always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cur_state <= IDLE;
        else 
            cur_state <= nxt_state;
    end
    
    always@(posedge clk or negedge rst_n)begin
        case(cur_state)
            IDLE:begin
                nxt_state = (data_valid==1  && data==0) ? A : IDLE;
            end
            A:begin
                if(data_valid)begin
                    if(data)
                        nxt_state = B;
                    else 
                        nxt_state = A;
                end
                else begin
                    nxt_state = A;
                end
            end
            B:begin
                if(data_valid)begin
                    if(data)
                        nxt_state = C;
                    else 
                        nxt_state = A;
                end
                else begin
                    nxt_state = B;
                end
            end
            C:begin
                if(data_valid)begin
                    if(data)
                        nxt_state = IDLE;
                    else 
                        nxt_state  =D;
                end
                else begin
                    nxt_state = C;
                end
            end
            D:begin
                if(data_valid)begin
                    if(data)
                        nxt_state = IDLE;
                    else 
                        nxt_state = A;
                end
                else begin
                    nxt_state = IDLE;
                end
            end
            
            default: nxt_state =IDLE;
        endcase
    end
    always@( cur_state)//这里同样地,要写成边沿触发的组合逻辑
       begin
         if(!rst_n)begin
            match <= 0;
        
         end
         else begin
             match <= (cur_state == D)? 1:0;
         end
        
        end
endmodule
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input data,
	input data_valid,
	output reg match
	);
    reg [3:0] data_r;
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            data_r <= 4'b0;
        else
            data_r <= data_valid? {data_r[2:0], data}: data_r;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            match <= 0;
        else
            match <= data_r[2:0]==3'b011 && data==0; // && ~match;
    end
    
endmodule

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
序列检测器是一种电路,用于检测输入信号序列是否符合某种特定的模式。在Verilog HDL语言中,可以使用状态机实现序列检测器。以下是一个简单的序列检测器的Verilog HDL代码示例: ```verilog module sequence_detector( input clk, // 时钟信号 input reset, // 复位信号 input data, // 输入数据信号 output reg match // 匹配信号 ); // 定义状态 typedef enum logic [1:0] { STATE_IDLE, // 空闲状态 STATE_S1, // 状态1 STATE_S2, // 状态2 STATE_S3 // 状态3 } state_t; // 定义状态转移表 parameter [3:0] TRANS_TABLE [0:3] = '{4'b0000, 4'b0001, 4'b0010, 4'b0100}; // 定义当前状态变量和下一个状态变量 reg [1:0] state, next_state; // 初始化状态为IDLE initial begin state = STATE_IDLE; end // 定义状态机逻辑 always @ (posedge clk, posedge reset) begin if (reset) begin state <= STATE_IDLE; end else begin state <= next_state; end end // 定义状态转移逻辑 always @ (*) begin case (state) STATE_IDLE: begin if (data) begin next_state = STATE_S1; end else begin next_state = STATE_IDLE; end end STATE_S1: begin if (!data) begin next_state = STATE_IDLE; end else if (data) begin next_state = STATE_S2; end end STATE_S2: begin if (!data) begin next_state = STATE_IDLE; end else if (data) begin next_state = STATE_S3; end end STATE_S3: begin if (!data) begin next_state = STATE_IDLE; match = 1; end else begin next_state = STATE_S3; end end endcase end endmodule ``` 这个序列检测器可以检测输入数据信号是否符合“1101”这个模式。输入数据信号通过data端口输入,匹配结果通过match端口输出。当输入数据信号符合“1101”这个模式时,match信号会被置为1。如果输入数据信号不符合模式,match信号会保持为0。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值