$$ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Self-defined math definitions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Math symbol commands \newcommand{\intd}{\,{\rm d}} % Symbol 'd' used in integration, such as 'dx' \newcommand{\diff}{{\rm d}} % Symbol 'd' used in differentiation ... $$

【FPGA学习】- 状态机

状态机

  状态机是组合逻辑和寄存器逻辑的特殊组合。一般包括两个部分,组合逻辑部分和寄存器逻辑部分。寄存器用于存储状态,组合电路用于状态译码和产生输出信号。状态机的下一个状态不仅和输入信号有关,而且和当前寄存器的状态也有关系。状态机的要素有三个:状态,输入输出

  状态:在逻辑设计中,使用状态划分逻辑顺序和时序规律。

  输入:状态机中进入状态的条件。

  输出:在某一个状态特定发生的事件。

  依据输出信号与输入信号是否有关,可以将状态机划分位摩尔型(Moore)状态机和米利型(Mealy)状态机。依据输出信号与输入信号是否同步,可以将状态机划分为同步状态机和异步状态机。

状态机描述方式

  状态机的描述方式有3种表示方法,状态转移图,状态转移表和编程语言描述。

  状态转移图:是状态机最自然的描述方式。使用图形化的方式来定义逻辑功能。

  状态转移表:使用列表的方式描述状态机,经常用于简化状态。

  编程语言描述:通过编程语言将状态机进行描述。

状态机设计原则

状态编码原则

  二进制编码:对确定的状态使用二进制数据进行编码。优点是使用的状态向量最少,简单;缺点是相邻状态切换时,可能有多位发生变化,瞬变次数多,易产生毛刺。

  格雷码:在相邻状态下,格雷码只有一位发生变化,虽然减少了毛刺的产生,但是格雷码并不适合有很多状态的情况。

  独热码:有多少个状态就有多少比特,而且只有一个比特为1,其他全为0的一种码制。独热码虽然多用了触发器,但是由于译码简单,有效提高了电路的速度和可靠性。

十进制数字码 独  热  码 十进制数字码 独  热  码
0 000_0000_00 5 000_0100_00
1 000_0000_01 6 000_1000_00
2 000_0000_10 7 001_0000_00
3 000_0001_00 8 010_0000_00
4 000_0010_00 9 100_0000_00
状态机容错处理

  在状态机的设计当中,不可避免地会出现大量的剩余状态。容错处理就是对这些剩余状态进行处理,以防止状态机进入不可预测的状态,出现短暂失控或始终无法摆脱剩余状态。常见的剩余状态处理方式如下。

  1.转入空闲状态,等待下一个工作任务的到来。

   2.转入指定状态去执行特定的任务。

  3.转入预定义的专门处理错误的状态。

  即在剩余状态下,将该状态转到一个我们能够确定的状态。比如if语句不漏掉else,case语句不漏掉default。

状态机设计准则

  设计要稳定。状态机不会进入死循环,即使进入非正常状态,也很快恢复正常。

  工作速度块。状态机尽可能满足电路的频率要求。尽可能用case语句代替if语句。

  所占资源少。尽可能使用较少的资源。

  代码易维护。代码书写要规范,做好文档维护。

摩尔型(Moore)状态机

  摩尔型时序电路的特点是当前的输出仅与当前电路状态有关,而与输入信号无关。最经典的例子就是红绿灯。考虑一个红绿灯控制器,按照日常功能完成“红灯(25s)->绿灯(15s)->黄灯(5s)”的变化。在状态转换图中,包含的要素有三个:状态数目,状态编码状态输出

  状态数目就是该时序电路中有几种状态,例如该控制器需要三种状态S0~S2。状态编码用于区分每个不同的状态,00表示红灯,01表示绿灯,10表示黄灯。状态输出就是描述在特定状态下的输出值,在斜线上,格式一般为“输出/输入”,表示的意义为在该输入的情况下,状态沿此箭头方向跳转并产生相应的输出。例如在该控制器中,从S0到S1,100表示亮起绿灯,由于没有输入信号,所以不写。010表示亮起黄灯,100表示亮起红灯。

   交通灯设计

module traffic_light(clock, reset, red, yellow, green);
    input clock, reset;
    output reg red, yellow, green;
    
    reg [1:0] current_state, next_state;    //存放状态
    reg [4:0] light_count, light_delay;    //增加计数器和计数器延迟
    
    parameter red_state = 2'b00,        //状态变量
          green_state = 2'b10,
          yellow_state = 2'b01,
          delay_red = 5'd25,        //状态持续时间
          delay_yellow = 5'd5,
          delay_green = 5'd15;

    always @(posedge clock, negedge reset) begin    //状态检测及转换
        if(reset == 1'b0)
            current_state <= red_state;
        else
            current_state <= next_state;
    end

    always @(*) begin
        case(current_state)
        red_state:begin
            light_delay = delay_red;
            if(light_count == light_delay)
            next_state = green_state;
        end
        green_state:begin
            light_delay = delay_green;
            if(light_count == light_delay)
            next_state = yellow_state;
        end
        yellow_state:begin
            light_delay = delay_yellow;
            if(light_count == light_delay)
            next_state = red_state;
        end
        default:begin
            next_state = red_state;
        end
        endcase
    end
    
    always @(posedge clock, negedge reset) begin    //输出信号
        if(reset==0) begin
            red <= 1;
            yellow <= 0;
            green <= 0;
        end
        else begin
            case(current_state)
            red_state:begin
            red <= 1;
                yellow <= 0;
                green <= 0;
            end
            green_state:begin
            red <= 0;
                yellow <= 0;
                green <= 1;
            end
            yellow_state:begin
            red <= 0;
                yellow <= 1;
                green <= 0;
            end
            endcase
        end
    end

    always @(posedge clock, negedge reset) begin    //计时
        if(reset==0)
            light_count <= 0;
        else if(light_count==light_delay)
            light_count <= 1;
        else
            light_count <= light_count + 1;
    end

endmodule

  激励文件

module test_traffic_light;
    reg clock, reset;
    wire red, green, yellow;

    traffic_light t1(clock, reset, red, yellow, green);    //实例引用

    initial clock = 0;        //产生时钟信号
    always #10 clock = ~clock;

    initial begin
        reset = 1;    //产生一个复位信号沿
       #1   reset = 0;
       #10  reset = 1;
       #10000;
       #20  $stop;
    end
endmodule;

  测试结果,可以看到按照红绿黄的顺序依次变换。

米利型(Mealy)状态机

  米利型电路的输出信号不仅与当前电路的状态有关,而且与输入也有关系,换言之,输入会决定电路转换的跳转。比如一个序列检测器,检测输入信号11010序列,我们可以知道状态转换图如下,S0表示初始态,用于区别其他状态。此状态下,若输入为1,跳转到下一状态S1,输出为0(只有检测到序列才输出为1),若输入为0,则继续在初态S0上,输出也为0;在S1状态下,表明此时为序列“1”,若此时输入为1,跳转到下一状态S2,输出为0,若输入为0,此时为序列“10”,与序列“11010”无法按照顺序吻合,故回到初态S0;在S2状态下,表明此时为序列“11”,若此时输入为0,跳转到下一状态S3,输出为0,若输入为1,此时为序列“111”,与序列“11010”可以按照顺序吻合,维持在状态S2;在S3状态下,表明此时为序列“110”,若此时输入为1,跳转到下一状态S4,输出为0,若输入为0,此时为序列“1100”,与序列“11010”无法按照顺序吻合,故回到初态S0;在S4状态下,表明此时为序列“1101”,若此时输入为0,跳转到下一状态S5,且输出为1,若输入为1,此时为序列“11011”,与序列“11010”可以按照顺序吻合,回到状态S2;在S5状态下,表明此时为序列“11010”,若此时输入为0,此时为序列“110100”,无法吻合,回到初态S0,输出为0,若输入为1,此时为序列“110101”,与序列“11010”可以按照顺序吻合,回到状态S1。

  序列检测器设计

module seq_detec(x, z, clk, reset);
    input x, clk, reset;
    output reg z;

    reg [2:0] current_state, next_state;

    parameter S0 = 3'd0,
          S1 = 3'd1,
          S2 = 3'd2,
          S3 = 3'd3,
          S4 = 3'd4,
          S5 = 3'd5;

    always @(posedge clk, negedge reset) begin        //状态转换
        if(reset == 0)
            current_state <= S0;
        else
            current_state <= next_state;
    end

    always @(*) begin                    //状态变化
        case(current_state)
        S0:begin
            if(x==1)
            next_state = S1;
            else
            next_state = S0;
        end
        S1:begin
            if(x==1)
            next_state = S2;
            else
            next_state = S0;
        end
        S2:begin
            if(x==0)
            next_state = S3;
            else
            next_state = S2;
        end
        S3:begin
            if(x==1)
            next_state = S4;
            else
            next_state = S0;
        end
        S4:begin
            if(x==0)
            next_state = S5;
            else
            next_state = S2;
        end
        S5:begin
            if(x==1)
            next_state = S1;
            else
            next_state = S0;
        end
        default:begin
            next_state = S0;
        end
        endcase
    end

    always @(posedge clk, negedge reset) begin    //产生输出
        if(reset == 0)
            z <= 0;
        else if(current_state == S5)
            z <= 1;
        else 
            z <= 0; 
    end
endmodule 

  激励设计

module test_seq_detec;
    reg x, clk, reset;
    wire z;
    integer seed = 8;

    seq_detec s1(x, z, clk, reset);

    initial clk = 0;        //初始化时钟信号
    always #5 clk = ~clk;

    initial begin
        reset = 1;
        #15 reset = 0;
        #15 reset = 1;
        @(posedge z);        //等待Z的上升沿,避免波形被直接节段
        #5 $stop;

    end
    
    always #10 x = ($random(seed) % 2);    //产生01随机数
endmodule

  结果,在最后产生了一个11010的序列,并产生了脉冲。 

 

参考资料

[1] 黄海,于斌,Verilog HDL 设计实用教程[M],北京:清华大学出版社,2021.

[2] 周润景,李志,张玉光,基于Quartus Prime的数字系统Verilog HDL设计实例详解(第3版)[M],北京:电子工业出版社,2018.

posted @ 2023-03-05 16:05  素衣叹风尘  阅读(800)  评论(0)    收藏  举报