HDLBits状态机(1)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

前言

        从今天开始打算记录一下作为一个小白学习FPGA的过程,以及第一次刷HDLBits遇到的一些我认为比较难的题目,记录一下日志方便自己复习,下面提供一些关于这些题目自己的看法,希望各位同样正在学习阶段的朋友一起讨论,并且指正我的错误。

题目 : Lemmings4 - HDLBits (01xz.net) 

HDLBits网站 : HDLBits (01xz.net)

一、Lemmings

       网站上面关于 Lemmings一共提供了4道题目,前三道只涉及到了比较简单的状态机,这里就不再赘述,直接提供一下前三道题目的代码供参考: 

1.  lemmings1

module top_module(
    input clk,
    input areset,    // Freshly brainwashed Lemmings walk left.
    input bump_left,
    input bump_right,
    output walk_left,
    output walk_right); //  

     parameter LEFT=0, RIGHT=1;
    reg state, next_state;

    always @(*) begin
        // State transition logic
        case(state)
            LEFT : next_state <= bump_left? RIGHT:LEFT;
            RIGHT: next_state <= bump_right? LEFT:RIGHT;
        endcase
    end

    always @(posedge clk, posedge areset) begin
        // State flip-flops with asynchronous reset
        if(areset)
            state <= LEFT;
        else
            state <= next_state;
    end

    // Output logic
    assign walk_left = (state == LEFT);
    assign walk_right = (state == RIGHT);

endmodule

2. lemmings2

         相较于上一道题多了掉落状态。

module top_module(
    input clk,
    input areset,    // Freshly brainwashed Lemmings walk left.
    input bump_left,
    input bump_right,
    input ground,
    output walk_left,
    output walk_right,
    output aaah ); 
    parameter L=0,R=1,LF=2,RF=3;
    reg [1:0]state,next_state;
    always@(*) begin
        case(state)
            L : next_state <= ground? (bump_left? R : L) : LF ;
            R : next_state <= ground? (bump_right? L : R ): RF ;
            LF : next_state <= ground? L : LF;
            RF : next_state <= ground? R : RF;
        endcase
    end
    always@(posedge clk or posedge areset) begin
        if(areset)
            state <= L;
        else
            state <= next_state;
    end
    assign walk_left = state == L;
    assign walk_right = state == R;
    assign aaah = (state == LF) || (state == RF);

endmodule

3. lemmings3

        相较于上一道题多了挖掘状态。

module top_module(
    input clk,
    input areset,    // Freshly brainwashed Lemmings walk left.
    input bump_left,
    input bump_right,
    input ground,
    input dig,
    output walk_left,
    output walk_right,
    output aaah,
    output digging ); 
    parameter L=0,R=1,LF=2,RF=3,LD=4,RD=5;
    reg [2:0]state,next_state;
    always@(*) begin
        case(state)
            L : next_state <= ground? (dig? LD:(bump_left? R:L )) : LF;
            R : next_state <= ground? (dig? RD:(bump_right? L:R )) : RF;
            LD : next_state <= ground? LD : LF;
            RD : next_state <= ground? RD : RF;
            LF : next_state <= ground? L : LF;
            RF : next_state <= ground? R : RF;
        endcase
    end
    always@(posedge clk or posedge areset) begin
        if(areset)
            state <= L;
        else
            state <= next_state;
    end
    assign walk_left = state==L;
    assign walk_right = state==R;
    assign aaah = (state==LF) || (state==RF);
    assign digging = (state==LD) || (state==RD);

endmodule

 这三道题的状态转换图作者已经给出,这里也不再重复画了,上面代码里面,我用 LD, LF 分别表示“向左走时挖掘”以及“向左走时掉落”两个状态,RD, RF同样,表示向右。

二、Lemmings4

这道题目是这一系列题目里面我认为比较难的。

题目描述

        虽然旅鼠可以走路、跌倒和挖掘,但旅鼠并非无懈可击。如果旅鼠跌落时间过长然后着地,它可能会飞溅。特别是,如果旅鼠掉落超过 20 个时钟周期然后撞到地面,它将飞溅并停止行走、跌落或挖掘(所有 4 个输出都变为 0),永远(或直到 FSM 重置)。旅鼠在落地前可以下落多远没有上限。旅鼠只有在落地时才飞溅;它们不会在半空中飞溅。

扩展有限状态机以对此行为进行建模。

跌落 20 个周期是可以生存的:

跌落 21 个周期会导致飞溅:

状态图

        这道题的状态相较于上面三道题大致相同,多了一个飞溅状态。除此之外还要计算20个时钟周期。那么写Verilog的时候,什么时候转换从跌落到飞溅的状态就比较重要(头疼)了。

下面放一张状态图,我画的太丑了凑合看吧:

我们首先就要想到用一个计数的变量 cnt 来计算脉冲(时钟上升沿)到达的数量,而且需要用这个变量来计算时钟周期是否到了20个。因此需要加一个时序的always块来计算每一个脉冲到达的时候脉冲的数量,并且判断是否到了20个。如果在下一次脉冲到达的时候、输入ground置位之后,发现脉冲数量就已经超过了20个,那么就要转换状态到飞溅状态了。(鼠鼠就寄了)

代码

        下面有两种计算时钟周期并转换状态的方式,写了这两种代码放在下面供参考:

第一种:

module top_module(
    input clk,
    input areset,    // Freshly brainwashed Lemmings walk left.
    input bump_left,
    input bump_right,
    input ground,
    input dig,
    output walk_left,
    output walk_right,
    output aaah,
    output digging ); 
    parameter L=0,R=1,LF=2,RF=3,LD=4,RD=5,X=6;
    reg [2:0]state,next_state;
    reg [4:0]cnt;
    always@(*) begin
        case(state)
            L : next_state <= ground? (dig? LD: (bump_left? R: L)):LF;
            R : next_state <= ground? (dig? RD: (bump_right? L: R)):RF;
            LD : next_state <= ground? LD : LF;
            RD : next_state <= ground? RD : RF;
            LF : next_state <= ground? ( (cnt==21)? X : L ) : LF;
            RF : next_state <= ground? ( (cnt==21)? X : R ) : RF;
            X : next_state <= X;
        endcase
    end
    
    always@(posedge clk or posedge areset) begin
        if(areset)
            cnt <= 0;
        else if(next_state==LF | next_state==RF)
            cnt <= (cnt>20)? cnt:cnt+1;
        else
            cnt <= 0;
    end
    
    always@(posedge clk or posedge areset) begin
        if(areset)
            state <= L;
        else
            state <= next_state;
    end
    assign walk_left = state==L;
    assign walk_right = state==R;
    assign aaah = state==LF || state==RF;
     assign digging = state==LD || state==RD;

endmodule

第二种:

module top_module(
    input clk,
    input areset,    // Freshly brainwashed Lemmings walk left.
    input bump_left,
    input bump_right,
    input ground,
    input dig,
    output walk_left,
    output walk_right,
    output aaah,
    output digging ); 
    parameter L=0,R=1,LF=2,RF=3,LD=4,RD=5,X=6;
    reg [2:0]state,next_state;
    reg [7:0]cnt;
    always@(*) begin
        case(state)
            L : next_state <= ground? (dig? LD: (bump_left? R: L)):LF;
            R : next_state <= ground? (dig? RD: (bump_right? L: R)):RF;
            LD : next_state <= ground? LD : LF;
            RD : next_state <= ground? RD : RF;
            LF : next_state <= ground? ( (cnt > 20)? X : L ) : LF;
            RF : next_state <= ground? ( (cnt > 20)? X : R ) : RF;
            X : next_state <= X;
        endcase
    end
    
    always@(posedge clk or posedge areset) begin
        if(areset)
            cnt <= 0;
        else if(next_state==LF | next_state==RF)
            cnt <= cnt+1;
        else
            cnt <= 0;
    end
    
    always@(posedge clk or posedge areset) begin
        if(areset)
            state <= L;
        else
            state <= next_state;
    end
    assign walk_left = state==L;
    assign walk_right = state==R;
    assign aaah = state==LF || state==RF;
     assign digging = state==LD || state==RD;

endmodule

 上面代码里面我用X表示旅鼠的飞溅状态。

代码解释

关于state和next_state

        首先要注意到上述两种方式,不管哪一种,计算cnt的always块里面的判断状态条件都是用next_state ( 这个if(next_state==LF||next_state==RF)),而不是直接用state判断状态,这是为什么呢?我个人理解如下:

        我们从题目作者给的时序图可以看到,当ground由1变为0的时候,输出会比ground输入晚一个周期,如果用next_state来判断的话,我们可以看成是在 ground置0的同一个周期,cnt开始计数,这样输出就对的上了:


        然后,我们写代码的时候肯定会想到首先cnt要满足这样下面的逻辑:

①cnt初始为0,如果ground由1变为0之后,只要 cnt > 20,那么就要转换state到“飞溅”;
②当ground由1变为0的时候,state从“走”变为“掉落”。

如果我们用state而不是next_state来判断:
        此时脉冲到达,由于非阻塞赋值的原因,计数always块里面的state还是上一个脉冲到达时的state,也就是“走”这个状态;
        那么这个时候,always块由于不满足条件就不执行了,会等到再下一个脉冲到达的时候才满足条件开始计数,也就是说cnt就会少数一个周期(少数了第0个周期,即从0~19),下面图应该能解释:


        如果我们在组合块代码里面的逻辑还是"cnt>20,state转换",那么会出现这样一种情况:

        旅鼠在跌落的第21个周期,ground置位,此时旅鼠本来应该寄了,但是我们用了state而不是next_state来判断,这个时候第21一个周期对应我们错误逻辑里面的第20个周期,旅鼠还会活着。

当然也可以用state来判断(我第一次就是这么写的,甚至感觉用state逻辑更好。。。。)用state的话上面代码里逻辑就要改成:

                            next_state <= ground? ( (cnt > 19)? X : L ) : LF;

关于这个问题在其他大佬的博客里面也学习到了一般写类似状态机以后都要用next_state来判断操作。。。。以后就养成良好习惯都用next_state。

关于非阻塞赋值,如果不理解的话,大家也可以看看另一道对输入信号进行边缘捕获寄存的题目,对输入信号的打拍操作。(我后面应该也会记录一下这道题)

关于为什么cnt要8bit

        另外,可能大家还有一个问题,20这个数只要5位寄存器就够了,为什么第二种方式里面,cnt要设置成8位的reg? 这里也给出我自己的理解:

        相较于第一种方式,第二种的逻辑更加简单,只需要在时序块里面cnt++就好了,但是如果我们只写了 reg [4:0]cnt 的话,会出现一种情况: 鼠鼠在下落远超过20个周期之后,ground才置位,这个时候我们的cnt就会溢出清零,那么状态就乱了,所以要多留一点位保存cnt。

如果理解了这个,第一种代码的方式也就能理解了,就是看如果cnt超过了20,到了第21个周期就要保存不变,然后组合逻辑块里面判断 cnt==21 是否为逻辑真,再进行状态转换。这种方式的话cnt只要[4:0]就好了。

后面还会继续更新其他我觉得需要记录的题目,有什么错误还请大家指正。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值