有限状态机的序列检测的Verilog实现思路(HDLbits_Exams/2014 q3fsm)

一、题目说明——HDLbits_Exams/2014 q3fsm

        Consider a finite state machine with inputs s and w. Assume that the FSM begins in a reset state called A, as depicted below. The FSM remains in state A as long as s = 0, and it moves to state B when s = 1. Once in state B the FSM examines the value of the input w in the next three clock cycles. If w = 1 in exactly two of these clock cycles, then the FSM has to set an output z to 1 in the following clock cycle. Otherwise z has to be 0. The FSM continues checking w for the next three clock cycles, and so on. The timing diagram below illustrates the required values of z for different values of w.

        Use as few states as possible. Note that the s input is used only in state A, so you need to consider just the w input.

有限状态机的序列检测的Verilog实现思路(HDLbits_Exams/2014 q3fsm)_第1张图片

网站:Exams/2014 q3fsm - HDLBits

二、题目分析

分析题目可知,我们需要设计的电路应满足以下特征:

1)功能:实现101、110、011序列的检测 。

2)端口:输入时钟clk、复位信号reset、s、w,输出检测信号z。

3)触发方式:时钟上升沿触发,reset同步且高电平有效。

4)时序解释说明

        1>时钟上升时触发电路复位后,进入A状态。

        2>时钟上升时若处于A状态,则检测当前输入s,如果s为高则进入B状态,否则仍在A状态。

        3>时钟上升时若处于B状态且寄存器为空,则寄存接下来三个时钟周期内的输入w。

        4>时钟上升时若处于B状态且寄存器为满,则清空寄存器,并接着寄存接下来的三个时钟周期内的输入w。(注意:此题目不是逐个移位判断,而是三个比特为一组判断)

        5>无论何时,一旦寄存器满足题目的比特要求(110/011/101)则将z置于高,否则置为低。这里需要注意:z是检测到就输出而不是等到下一个时钟再输出,为了简单起见,推荐使用assign语句对输出z赋值。

三、实现与总结

        我们使用寄存器变量w_reg来完成接下来三个时钟周期内的w值的寄存,除此之外也可以使用计数实现。

        这里需要注意一个细节,对于3位宽的寄存器变量,存在一个初始化的问题,如果一开始初始化为‘b000,那么一旦复位后连续两个时钟周期内都检测到w为1,则寄存器就已经为011了,满足了输出z为1的条件,但这是不正确的,z的输出必须是等到三个周期检测完之后再输出。同样的,如果初始化寄存器为111,只要第一个时钟到来时检测到w为0,就会输出z为1了,这样看来,无论如何初始化寄存器的值,这种情况都不可避免。

有限状态机的序列检测的Verilog实现思路(HDLbits_Exams/2014 q3fsm)_第2张图片

        仔细考虑后,我们发现这个问题的本质在于电路无法区别这是第几个时钟周期,从而导致没有将三个时钟检测完之后再输出,而是在任何时候满足情况就会输出。改进它有两个思路。

改进思路一        改变寄存器寄存内容即对w编码。由于寄存器的值对于初始化状态、w=0状态、w=1状态的结果不一样,所以至少使用两位比特表示三个不同状态。这里初始化寄存器的值依然是6’b0。但是每次寄存的内容是二位比特{~w,w},即w=0时,我们寄存2’b10;w=1,我们寄存2‘b01;而2’b00就是未赋值的情况,这样电路就能区分寄存器是否全部被赋值过了,但是这种方法比较复杂。

module top_module (
    input clk,
    input reset,   // Synchronous reset
    input s,
    input w,
    output z
);

    reg [1:0] state;
    reg [5:0] w_reg;
    assign z = (((w_reg[5:4]=='b10)&(w_reg[3:2]=='b01)&(w_reg[1:0]=='b01))|
        			((w_reg[5:4]=='b01)&(w_reg[3:2]=='b01)&(w_reg[1:0]=='b10))|
                    ((w_reg[5:4]=='b01)&(w_reg[3:2]=='b10)&(w_reg[1:0]=='b01)));
    
    localparam A=0,B_detect_1=1,B_detect_2=2,B_detect_3=3;
    
    always@(posedge clk)begin
        if(reset)begin
            state<=A;w_reg<=0;
        end
        else begin
            case(state)
                A:state<=s;
                B_detect_1:begin w_reg<={4'b0000,~w,w};   state<=B_detect_2;end 
                B_detect_2:begin w_reg<={w_reg[3:0],~w,w};state<=B_detect_3;end
                B_detect_3:begin w_reg<={w_reg[3:0],~w,w};state<=B_detect_1;end
                default:state<=A;
            endcase
        end
    end
endmodule

改进思路二        改变输出z的时刻,让z总是在第三个时钟检测后再输出,其他时刻z均不输出。这种方法比较简单。但需要注意:并不是当state=B _detect_3时输出z,因为在这个状态下,寄存器还未满,应该是它的下一个状态即B_detect_1。

module top_module (
    input clk,
    input reset,   // Synchronous reset
    input s,
    input w,
    output z
);

    reg [1:0] state;
    reg [2:0] w_reg;
    assign z = (state==B_detect_1)&(
                (w_reg[2]&w_reg[1]&~w_reg[0])|
                (w_reg[2]&~w_reg[1]&w_reg[0])|
                (~w_reg[2]&w_reg[1]&w_reg[0]));
  
    localparam A=0,B_detect_1=1,B_detect_2=2,B_detect_3=3;
    
    always@(posedge clk)begin
        if(reset)begin
            state<=A;w_reg<='b0;
        end
        else begin
            case(state)
                A:state<=s;
                B_detect_1:begin w_reg<={2'b00,w};     state<=B_detect_2;end 
                B_detect_2:begin w_reg<={w_reg[1:0],w};state<=B_detect_3;end
                B_detect_3:begin w_reg<={w_reg[1:0],w};state<=B_detect_1;end
                default:state<=A;
            endcase
        end
    end
endmodule

        当然使用计数的实现方式和寄存器寄存w的方式本质上是一样,都是寄存了当前输入w的状态,并在第三个时钟到来时决定输出z的值。

你可能感兴趣的:(Verilog学习笔记,fpga开发)