verilog 自动售货机状态机实现_【划水】怎么写一个有限状态机?

976b7534a5d2f690e2e441c3ae3d7765.png

太久没回学校了,甚至想念实验室了,实验室的感觉呢,实验室比家里好door♂了,在家里一个人很无聊,又没有板子玩、又没有仪器用,进了实验室里边个个是人才,说话又好听,哦哟~超喜欢在里边的。

但由于疫情原因,呆在家里没办法,只能趁机划几篇水文才能维持得了生活这样子。这次简单讲讲有限状态机的事情!

6bb17a35929ffdc4f4b202c975dada53.png
“科研是不可能科研的,这辈子都不可能科研的”

啥叫有限状态机?

有限状态机(Finite State Machine,FSM),就是指有限个状态以及在这些状态之间的转移和动作等行为的数学模型。举个栗子便于解释,比如“领袖”要去偷电瓶,需要完成:寻找电瓶车查看周边环境下手骑上就跑等四个步骤,如果将这一系列步骤抽象成为数学模型,可如下图所示

130b5663ea3c2356417fc1adb874f4f0.png
“领袖偷电瓶”状态机

在图中,可以注意到:

  1. 状态是有限的,图中一共有“寻找电瓶车”、“查看周边环境”、“下手”和“骑上就跑”四个状态;
  2. 状态转移是根据触发条件来确定的,例如找到电瓶车后,状态就从“寻找电瓶车”状态转移到“查看周边环境”状态;
  3. 状态转移也限于既定状态内,例如该状态机就只在这四个状态里转移;
  4. 处于某个状态下的行为是持续的,例如“下手”状态中就一直在开锁等;
  5. 每个状态的输出结果可能不是一样的,例如直到“骑上就跑”状态时,才获得一辆电瓶车。

有了这样的概念之后,我们就可以在某些设计中就使用有限状态机来实现所需的功能。

需要注意的是

根据状态机的输出是否与输入有关,可以分为下列两种状态机:

  1. Moore型:输出只与当前状态有关,与输入无关;
  2. Mealy型:输出不仅与当前状态有关,还与输入有关。

这两种状态机在电路实现上的区别如下图所示[1],当然对这种状态机的具体区别和转换等可以参考[1]

fb71552358cdf3fff9948b03304a4f7b.png
Moore型状态机

b1c00280e645760f69e16f9b869158b8.png
Mealy型状态机

此外,对各个状态进行编码,又有以下几种编码方式:

  1. 二进制:根据状态数量进行二进制累加编码;
  2. 格雷码:相邻状态的编码只差1bit;
  3. 热独码:每个状态的编码只有1bit为1,其他为0;

分别采用这三种编码对上述四个状态进行编码,如下图所示

81572c85ee5dd56f3e69030a25b311eb.png

可以明显看到,二进制和格雷码比热独码更节省寄存器,但热独码具有节省组合逻辑的优点[2],热独码也是在状态机的设计当中较为常用的状态编码方式。

那我该咋写状态机?

比如问“输入串行序列,用状态机实现‘101’序列检测,若序列符合则输出1,否则输出0“,使用Verilog实现的具体流程是:

  1. 确定各状态:按照问题可以确定S0、S1、S2、S3四种状态,分别表示序列为xxx、xx1、x10、101四种情况,并采用热独码对状态编码,例如S0:4'b0001、S1:4'b0010、S2:4'b0100、S3:4'b1000;
  2. 确定状态转换条件:根据1可知,当序列输入为1时,S0->S1,当序列输入为0时,S1->S2,以此类推;
  3. 确定状态输出:根据1可知,当S0时输出0,S1时输出0,S2时输出0,S3时输出1。

以下分别采用三种常见的状态机实现上述要求:

  • 一段式:直接粗暴写着爽

一段式即将“状态转移、状态输出和状态转移条件”写在一块,简单粗暴,写的飞起~

    //一段式状态机
    reg     [3:0]   state;
    localparam      S0  =   4'b0001,    //采用热独码
                    S1  =   4'b0010,
                    S2  =   4'b0100,
                    S3  =   4'b1000;

    //共一段 采用时序逻辑完成状态机的转移、输出等
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            // reset
            dout    <=  1'b0;
            state   <=  S0;
        end
        else begin
            case(state)
                S0:begin
                    dout    <=  1'b0;   //输出0
                    if (din == 1'b1) begin
                        state   <=  S1;
                    end
                    else begin
                        state   <=  S0;
                    end
                end

                S1:begin
                    dout    <=  1'b0;   //输出0
                    if (din == 1'b1) begin
                        state   <=  S1;
                    end
                    else begin
                        state   <=  S2;
                    end
                end

                S2:begin
                    dout    <=  1'b0;   //输出0
                    if (din == 1'b1) begin
                        state   <=  S3;
                    end
                    else begin
                        state   <=  S0;
                    end
                end

                S3:begin
                    dout    <=  1'b1;   //输出1
                    if (din == 1'b1) begin
                        state   <=  S1;
                    end
                    else begin
                        state   <=  S2;
                    end
                end

                default:begin
                    dout    <=  1'b0;
                    state   <=  S0;
                end
            endcase
        end
    end

一段式的状态机的RTL如下图所示:

6e524563f6a395937228118086be3a46.png
一段式状态机RTL

一段式的状态机的RTL综合后的结果如下图所示:

754c51410866c632bd65ef2dacca82d6.png
一段式状态机RTL综合后的结果
  • 两段式:容易理解不难写⭐⭐⭐

两段式即将“状态转移”、“状态输出和状态转移条件”分成两部分写,容易理解,便于维护。(经评论区提醒组合逻辑不用复位)

    //两段式状态机
    reg     [3:0]   current_state;      //当前状态
    reg     [3:0]   next_state;         //下一状态
    localparam      S0  =   4'b0001,    //采用热独码
                    S1  =   4'b0010,
                    S2  =   4'b0100,
                    S3  =   4'b1000;
    
    //第一段 采用时序逻辑完成状态转移
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            // reset
            current_state   <=  S0;
        end
        else begin
            current_state   <=  next_state;
        end
    end

    //第二段 采用组合逻辑完成转移条件判断和状态输出
    always @(*) begin
        case(current_state)
            S0:begin
                dout    =  1'b0;   //输出0
                if (din == 1'b1) begin
                    next_state   =  S1;
                end
                else begin
                    next_state   =  S0;
                end
            end

            S1:begin
                dout    =  1'b0;   //输出0
                if (din == 1'b1) begin
                    next_state   =  S1;
                end
                else begin
                    next_state   =  S2;
                end
            end

            S2:begin
                dout    =  1'b0;   //输出0
                if (din == 1'b1) begin
                    next_state   =  S3;
                end
                else begin
                    next_state   =  S0;
                end
            end

            S3:begin
                dout    =  1'b1;   //输出1
                if (din == 1'b1) begin
                    next_state   =  S1;
                end
                else begin
                    next_state   =  S2;
                end
            end

            default:begin
                dout        =  1'b0;
                next_state  =  S0;
            end
        endcase
    end

两段式的状态机的RTL如下图所示:

591963308c53ea179e3d01120666cd12.png
两段式状态机RTL

两段式的状态机的RTL综合后的情况如下图所示:

690c374d7938d5a6c91b48613dd5a822.png
两段式状态机RTL综合后的结果
  • 三段式:结构精简八股文⭐⭐⭐⭐⭐

三段式即将“状态转移”、“转移条件”、“状态输出”分三部分写,可能在看起来会比较的难写,但是了解结构之后感觉就像八股文或是填空题一样。但需要注意的是:第三段不一定是采用一个always完成,如果输出的信号较多可以采用多个。(经评论区提醒组合逻辑不用复位)

    //三段式状态机
    reg     [3:0]   current_state;      //当前状态
    reg     [3:0]   next_state;         //下一状态
    localparam      S0  =   4'b0001,    //采用热独码
                    S1  =   4'b0010,
                    S2  =   4'b0100,
                    S3  =   4'b1000;
    
    //第一段 采用时序逻辑完成状态转移
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            // reset
            current_state   <=  S0;
        end
        else begin
            current_state   <=  next_state;
        end
    end

    //第二段 采用组合逻辑完成转移条件判断
    always @(*) begin
        case(current_state)
            S0:begin
                if (din == 1'b1) begin
                    next_state   =  S1;
                end
                else begin
                    next_state   =  S0;
                end
            end

            S1:begin
                if (din == 1'b1) begin
                    next_state   =  S1;
                end
                else begin
                    next_state   =  S2;
                end
            end

            S2:begin
                if (din == 1'b1) begin
                    next_state   =  S3;
                end
                else begin
                    next_state   =  S0;
                end
            end

            S3:begin
                if (din == 1'b1) begin
                    next_state   =  S1;
                end
                else begin
                    next_state   =  S2;
                end
            end

            default:next_state  =  S0;
        endcase
    end

    //第三段 采用时序逻辑状态输出
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            // reset
            dout    <=  1'b0;
        end
        else begin
            case(next_state)    //采用next state,提前一拍
                S0:begin
                    dout    <=  1'b0;   //输出0
                end

                S1:begin
                    dout    <=  1'b0;   //输出0
                end

                S2:begin
                    dout    <=  1'b0;   //输出0
                end

                S3:begin
                    dout    <=  1'b1;   //输出1
                end

                default:dout    <=  1'b0;
            endcase
        end
    end

三段式的状态机的RTL如下图所示:

f5f14bb1380acca862c7d7693968d8a1.png
三段式状态机RTL

三段式的状态机的RTL综合后的结果如下图所示:

c704a3009f98d6b266cbe6f9dc7c4455.png
三段式状态机RTL综合后的结果

总的来说,三段式状态机是较为常用的写法。


我想是以后带【划水】都是随便写写的,可能内容比较简单,当然写的问题还望批评指正~

参考

  1. ^ab整理 | FSM 有限状态机 http://www.airbird.info/2017/fpga-fsm/
  2. ^FPGA编写有限状态机使用独热码为什么会占用较少的组合逻辑电路? https://www.zhihu.com/question/40994717
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值