FPGA-状态机

状态机概念

硬件设计很讲究并行设计思想,虽然verilog描述的电路大多是并行的,但是对于实际的工程应用中,往往需要设计硬件来实现一些具有顺序的工作,就需要用到状态机的思想。

状态机:有限状态机(Finite State Machine),简称FSM。

状态机是数字系统设计中非常重要的组成部分,状态机的设计对系统的高可靠性、高速性有着至关重要的作用。

状态机由多个相互跳转的状态组成,用于对具有逻辑顺序和时序规律的事件进行描述,在任意时刻,状态机只能处于某一个状态;状态机的功能可以发分解为两个部分:第一部分根据外部输入实现状态转移;第二部分根据特定状态和输入来驱动输出。

状态机可以将待实现的复杂功能分解在各个状态中,使得整个系统的控制流程的实现变得简单化。

状态机有四个要素:现态、次态、输入、输出;

现态:状态机当前所处的状态;

次态:根据现态、输入、状态转移条件所得到的状态机将要跳转至的新状态;

输入:一般指外部事件,当一个外部事件发生后,状态机便会根据状态转移函数发生响应;

输出:由现态或现态和输入共同决定的,但是有时某一状态或者某一输入改变,输出并不一定会发生变化,带来的仅仅是状态迁移而已。

状态机模型

摩尔型状态机:

米勒型状态机:

由于摩尔型状态机的输出来自组合逻辑,可能会产生毛刺,从而产生不利的影响;

而米勒型状态机由于输入与输出有关,且输出也是组合逻辑,输出时序不佳,限制了系统的工作频率;

更好的方法时把摩尔型状态机和米勒型状态机混合起来使用,也即输出既和状态有关又与输入有关,采用时序逻辑输出。

寄存器型输出:具有更好的时序性能。

注意:一个健壮的状态机应当具备初始化状态或默认状态;即芯片上电或复位之后,状态机能够自动将所有判断条件复位,并进入初始状态。

大多数FPGA都有GSR(全局复位信号),当FPGA上电后,GSR有效,对所有寄存器、RAM等单元复位/置位。

状态机描述方法

状态机描述方法:一段式、两段式、三段式

一段式状态机:所有的状态变化以及输出变化都写在一个always块中在该always块中既描述状态的同步转移,又描述状态的输入条件和输出。

两段式状态机:用两个always块来描述状态机。其中一个模块采用同步时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件。它需要两个状态——现态和次态,然后通过现态和次态的转换来实现时序逻辑。输出采用组合逻辑。

三段式状态机:用三个always块来描述状态机。其中一个模块采用同步时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件(注意和两段式的区别)。第三个模块描述状态的输出(既可以用组合逻辑也可以用时序逻辑)。

可见,三段式描述法是最简单的。

状态机编码方法

状态机编码方法:独热码编码(只有一位是高电平),自然二进制编码,格雷码编码。

FPGA提供的触发器资源比组合逻辑资源多,所以使用独热码编码较好,节省组合逻辑资源,简化了比较逻辑,状态译码方便。

总结:

推荐使用独热码编码方式与三段式描述方式设计状态机。

1.三段always块中,第一个和第三个(多个)always块都是同步时序逻辑,用非阻塞(<=)赋值;第二个always块是组合逻辑,用阻塞赋值(=) ;

2.第二部分为组合逻辑always块,建议敏感列表使用always@(*)的方式;

3.第二部分组合逻辑always块中的判断条件一定要包含所有情况;

4.第二部分组合逻辑always块中的case中的条件应该为当前状态-state c;

5.状态转移的条件可以在第二段组合逻辑always块之外单独设计。

序列检测设计

基于状态机的概念,设计下面一个状态图:

此状态图是要在一段序列101101101101中检测出1101的序列,可重复检测3次1101。

设计随机输入一段序列,这个序列我们可以用按键去传达,比如这里,由于序列是二进制序列只有0个1组成,因此设计两位按键用高位和低位分别表示是否输出1/0,高位表示是否输出1,低位表示是否输出0。输出dout则规定4位,检测是否有1101的序列输出。

设计代码如下:

module detect (
    input              clk        ,
    input              rst_n      ,
    input      [1:0]   key_in     ,

    output reg [3:0]   dout             
);
//参数定义
parameter        S1 = 4'b0001  ,//检测‘1’
                 S2 = 4'b0010  ,//检测‘1’
                 S3 = 4'b0100  ,//检测‘0’
                 S4 = 4'b1000  ;//检测‘1’

//状态机信号
    reg  [3:0]   state_c       ;
    reg  [3:0]   state_n       ; 

    wire         s12s2         ;
    wire         s22s3         ;
    wire         s22s1         ;
    wire         s32s4         ;
    wire         s42s2         ;
    wire         s42s1         ;

//第一段 时序逻辑 状态的转移
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state_c <= S1 ;
    end
    else begin
        state_c <= state_n ;
    end
end

//第二段 组合逻辑 描述状态转移的规律
always @(*) begin
    case (state_c)
        S1:begin
            if(s12s2)
            state_n = S2 ;
            else
            state_n = state_c ;
        end
        S2:begin
            if(s22s3)
            state_n = S3 ;
            else if(s22s1)
            state_n = S1 ;
            else
            state_n = state_c ;
        end
        S3:begin
            if(s32s4)
            state_n = S4 ; 
            else
            state_n = state_c ;
        end
        S4: begin
            if(s42s2)
            state_n = S2 ;
            else if(s42s1)
            state_n = S1 ;
            else
            state_n = state_c ;
        end
        default:state_n = S1 ; 
    endcase
end

//状态之间的条件(状态之间有多少箭头就有多少条件)
assign      s12s2 = state_c == S1 && (key_in[1]);//1
assign      s22s3 = state_c == S2 && (key_in[1]);//1-1
assign      s22s1 = state_c == S2 && (key_in[0]);//1-0
assign      s32s4 = state_c == S3 && (key_in[0]);//1-1-0
assign      s42s2 = state_c == S4 && (key_in[1]);//1-1-0-1
assign      s42s1 = state_c == S4 && (key_in[0]);//1-1-0-0

//dout
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        dout <= 'd0;
    end
    else if (s42s2) begin
        dout <= dout + 1'b1; //输出需要的结果1101
    end
end

endmodule

测试文件:

`timescale 1ns/1ps
module detect_tb();
reg                clk      ;
reg                rst_n    ;
reg      [1:0]     key      ;//两个按键0/1表示的是这两个按键是否按下,如0/1表示低位按键按下而高位按键没按下

wire     [3:0]     dout     ;

//参数
parameter    CYCLE = 20 ;//50M时钟 


//模块例化
detect u_detect(
    .clk                (clk    ),
    .rst_n              (rst_n  ),
    .key_in             (key    ),
    .dout               (dout)  
);

//激励
initial begin  //产生时钟
    clk = 1'b1 ;
    forever begin
        #(CYCLE/2);
        clk = ~clk ;
    end
end

integer i;
initial begin
    rst_n = 1'b1;
    #(CYCLE);
    rst_n = 1'b0;

    key = 2'b00;
    #(CYCLE*2);
    #2;
    rst_n = 1'b1;
    #(CYCLE*4)

    for (i = 0 ;i < 10 ; i = i + 1 ) begin
        key[0] = {$random}%2 ;
        #(CYCLE);
        key = 2'b00;
        #(CYCLE);
        key[1] = {$random}%2 ;   
        #(CYCLE);
        key = 2'b00;
        #(CYCLE*2);
    end

    key = 2'b00;

    #(CYCLE*10);
    $stop;
end
endmodule

仿真分析:

仿真结果显示,此段随机序列没有一个符合要求的1101序列。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值