verilog3 HDLbits:Moore型有限状态机(FSM)verilog实现简单旅鼠游戏(lemmings game)

本文详细介绍了有限状态机(FSM)的概念及其在旅鼠游戏中的应用,通过逐题解析如何构建Moore型状态机来模拟旅鼠的行为变化。包括状态转移逻辑、独热码编码以及时序逻辑设计。最后总结了解题过程中使用的通用结构和编程技巧。
摘要由CSDN通过智能技术生成


前言

本文主要简单介绍有关FSM(有限状态机)的相关内容,并通过讲解HDLbits上的一道题目lemmings game(旅鼠游戏)介绍Moore型有限状态机的一般解决方法并详述其思路。


一. 有限状态机(Finite State Machine)

1. 介绍

状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。
以输出与状态和输入的关系有限状态机可以分为两类:
1.若输出只和状态有关而与输入无关,则称为Moore型状态机;
2.输出不仅和状态有关而且和输入有关系,则称为Mealy型状态机;
关于状态机的一个极度确切的描述是:它是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而“运行”。每个事件都在属于“当前” 节点的转移函数的控制范围内,其中函数的范围是节点的一个子集。函数返回“下一个”(也许是同一个)节点。这些节点中至少有一个必须是终态。当到达终态, 状态机停止。
什么是状态机

2. 状态机四要素

状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果:
①现态:是指当前所处的状态。
②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

3. 举例说明

画出lemmings1的状态转换图,如下图所示,可以清晰看出状态的转换。按四要素分析,我们假定walk_left为初态,若事件bump_left=1条件满足,则执行动作迁移到walk_right,则walk_right就是我们假定的walk_left的次态,满足状态机的四要素。其他状态转换也可按此分析。
lemmings1

二. 题目

1. lemmings1

lemmings1

1)翻译:

旅鼠游戏涉及大脑相当简单的小动物。如此简单,以至于我们将使用有限状态机对其进行建模。在旅鼠的 2D 世界中,旅鼠可以处于两种状态之一:向左行走或向右行走。如果它撞到障碍物,它会改变方向。特别是,如果旅鼠在左边被撞到,它会向右走。如果它在右边撞到,它会向左走。如果它同时在两侧碰撞,它仍然会改变方向。
实现具有两个状态、两个输入和一个输出的摩尔状态机,用于模拟此行为。

2)题解:

1 . 由题目可知旅鼠可以处于两种状态,向左或者向右行走,我们定义两个parameter参数(LEFT=0,RIGHT=1)分别表示这两种状体,在定义两个reg变量用来存放当现态和次态(state,next_state)。

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

2.然后我们需要确定状态的逻辑变换,即确定某个状态的次态,我们可以用一个组合逻辑块。

/// This is a combinational always block
    always @(*) begin
        // State transition logic
        if(state)  next_state=bump_right?LEFT:RIGHT;//如果在右行走时碰到墙则改变方向向左
        else       next_state=bump_left ?RIGHT:LEFT;//如果在左行驶时碰到墙则改变方向向右
    end

3.我们需要一个具有异步复位的状态触发器,我们使用一个时许逻辑块。

always @(posedge clk, posedge areset) begin
        if(areset) state<=LEFT;
        else       state<=next_state;
    end

4.输出,我们用assign语句赋值即可。

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

5.完整代码如下:

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
        if(state)  next_state=bump_right?LEFT:RIGHT;
        else       next_state=bump_left ?RIGHT:LEFT;
    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

1

2. lemmings2

lemmings2

1)翻译:

除了左右行走外,旅鼠还会倒下(大概会“aaah!”),如果地面消失在它们下面。除了左右行走和颠簸时改变方向外,当ground=0时,旅鼠会摔倒并说“aaah”!当地面重新出现(ground=1)时,旅鼠将恢复以与坠落前相同的方向行走。跌倒时被磕碰不影响行走方向,与地面消失(但尚未下落)或地面再次出现时仍下落时被磕碰,也不影响行走方向。构建一个有限状态机来模拟这种行为。

2)题解:

思路不过多赘述,过程与lemmings1一致,相当于是lemmings1的扩展,此时我们考虑到除去lemmings1的左右行走的状态之外,还需要考虑向右行走时掉下去和向左行走时掉下去的两种状态,则共有4种状态。下面直接放完整代码。
完整代码:

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 LEFT=4'b0001,RIGHT=4'b0010,LEFT_FALL=4'b0100,RIGHT_FALL=4'b1000;
    reg [3:0] state,next_state;
    
    always @(*) begin
        case(state)
            LEFT: 
                if(ground)   next_state <= bump_left?RIGHT:LEFT;
                else         next_state <= LEFT_FALL;
            RIGHT:
                if(ground)   next_state <= bump_right?LEFT:RIGHT;
                else         next_state <= RIGHT_FALL;
            LEFT_FALL:
                if(ground)   next_state <= LEFT;
                else         next_state <= state;
            RIGHT_FALL:
                if(ground)   next_state <= RIGHT;
                else         next_state <= state;
            default:         next_state <= LEFT;
        endcase
    end
        
        always @(posedge clk or posedge areset) begin
            if(areset)   state<=LEFT;
            else         state<=next_state;
        end
        
        assign walk_left  = (state==LEFT);
        assign walk_right = (state==RIGHT);
        assign aaah       = (state==LEFT_FALL |state==RIGHT_FALL);//只要掉落就会ahhh

endmodule

22

3)独热码简述:

此时对这四个状态的编码使用独热码表示,One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。如果使用二进制或格雷码来代表状态,则需要用到解码器才能得知该码代表的状态。使用独热码来代表状态的话,则不需要解码器,因为若第n个位元为1,就代表机器目前在第n个状态。在本题中没有那么严格要求,也可用十进制1,2,3,4来表示,一般写状态机时我们用独热码可能更多。

3. lemmings3

lemmings3

1)翻译:

除了走路和跌倒之外,旅鼠有时还可以被告知做一些有用的事情,比如挖掘(当dig=1时它开始挖掘)。如果旅鼠当前在地面上行走(ground=1且不掉落),则可以挖掘,并将继续挖掘,直到到达另一侧(ground=0)。在这一点上,由于没有地面,它会掉下来(aaah!),然后在它再次落地后继续朝着原来的方向走。与跌倒一样,挖掘时被撞到没有效果,在跌倒或没有地面时被告知要挖掘则被忽略。换句话说,行走的旅鼠可以跌倒、挖掘或改变方向。如果满足这些条件中的一个以上,则 fall 的优先级高于 dig,而 dig 的优先级高于切换方向。扩展有限状态机以模拟此行为。

2)题解:

分析题目得出对比lemmings2又多了两个状态,LEFT_DIG and RIGHT_DIG,总共六个状态,用六位独热码编码,按照lemmings2的基础上进行扩展。
完整代码:

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   LEFT      = 6'b000001,
                RIGHT     = 6'b000010,
                LEFT_FALL = 6'b000100,
                RIGHT_FALL= 6'b001000,
                LEFT_DIG  = 6'b010000,
                RIGHT_DIG = 6'b100000;
    reg [5:0] state,next_state;
    
    always @(*) begin
        case(state)
            LEFT:
                if(ground)
                    next_state=dig?LEFT_DIG:(bump_left?RIGHT:LEFT);
                else
                    next_state=LEFT_FALL;
            RIGHT:
                if(ground)
                    next_state=dig?RIGHT_DIG:(bump_right?LEFT:RIGHT);
                else
                    next_state=RIGHT_FALL;
            LEFT_FALL:
                if(ground)
                    next_state=LEFT;
                else
                    next_state=state;
            RIGHT_FALL:
                if(ground)
                    next_state=RIGHT;
                else
                    next_state=state;
            LEFT_DIG:
                if(ground)
                    next_state=state;
                else
                    next_state=LEFT_FALL;
            RIGHT_DIG:
                if(ground)
                    next_state=state;
                else
                    next_state=RIGHT_FALL;
            default:
                    next_state=LEFT;
        endcase
    end
    
    always @(posedge clk or posedge areset) begin
        if(areset) state<=LEFT;
        else       state<=next_state;
    end
    
        assign walk_left =(state==LEFT);
        assign walk_right=(state==RIGHT);
        assign aaah      =(state==LEFT_FALL|state==RIGHT_FALL);
        assign digging   =(state==LEFT_DIG|state==RIGHT_DIG);
            
endmodule

33

4. lemmings4

lemmings4

1)翻译:

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

2)题解:

1)分析并与lemmings3进行比较,多了一个鼠鼠会死亡的设定,但掉落超过20个时钟周期撞击到地面它会死亡,所以我们需要再多添加一个计数器来计数鼠鼠掉落的时钟数。再对lemmings3的代码进行一些小扩展即可。
计数器模块代码:

    reg[8:0] count_time;
    always @(posedge clk or posedge areset) begin
        if(areset)   count_time<=8'd0;
        else 
            if(state==LEFT_FALL |state==RIGHT_FALL) //只要跌落就算
                count_time<=count_time+8'd1;
            else  count_time<=8'd0;
    end

2) 需要注意的是最后的输出,当鼠鼠死亡后会停止行走,挖掘,掉落,四个输出(walk_left,walk_right,aaah,digging)全部为0。故每个输出都要考虑鼠鼠是否死亡。

        assign walk_left =(state!=DEATH)&(state==LEFT);
        assign walk_right=(state!=DEATH)&(state==RIGHT);
        assign aaah      =(state!=DEATH)&(state==LEFT_FALL|state==RIGHT_FALL);
        assign digging   =(state!=DEATH)&(state==LEFT_DIG|state==RIGHT_DIG);

完整代码:

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   LEFT      = 7'b0000001,
                RIGHT     = 7'b0000010,
                LEFT_FALL = 7'b0000100,
                RIGHT_FALL= 7'b0001000,
                LEFT_DIG  = 7'b0010000,
                RIGHT_DIG = 7'b0100000,
                DEATH     = 7'b1000000;

    parameter DEATH_TIME = 8'd19;
    reg[6:0] state,next_state;
    reg[8:0] count_time;
    always @(posedge clk or posedge areset) begin
        if(areset)   count_time<=8'd0;
        else 
            if(state==LEFT_FALL |state==RIGHT_FALL)
                count_time<=count_time+8'd1;
            else  count_time<=8'd0;
    end
    always @(*) begin
        case(state)
            LEFT:
                if(ground)
                    next_state=dig?LEFT_DIG:(bump_left?RIGHT:LEFT);
                else
                    next_state=LEFT_FALL;
            RIGHT:
                if(ground)
                    next_state=dig?RIGHT_DIG:(bump_right?LEFT:RIGHT);
                else
                    next_state=RIGHT_FALL;
            LEFT_FALL:
                if(ground)
                    if(count_time<=DEATH_TIME)
                        next_state=LEFT;
                    else 
                        next_state=DEATH;
                else
                    next_state=state;
            RIGHT_FALL:
                if(ground)
                    if(count_time<=DEATH_TIME)
                        next_state=RIGHT;
                    else 
                        next_state=DEATH;
                else
                    next_state=state;
            LEFT_DIG:
                if(ground)
                    next_state=state;
                else
                    next_state=LEFT_FALL;
            RIGHT_DIG:
                if(ground)
                    next_state=state;
                else
                    next_state=RIGHT_FALL;
            DEATH:
                    next_state=state;
            default:
                    next_state=LEFT;
        endcase
    end
    always @(posedge clk or posedge areset) begin
        if(areset)   state<=LEFT;
        else         state<=next_state;
    end
    
        assign walk_left =(state!=DEATH)&(state==LEFT);
        assign walk_right=(state!=DEATH)&(state==RIGHT);
        assign aaah      =(state!=DEATH)&(state==LEFT_FALL|state==RIGHT_FALL);
        assign digging   =(state!=DEATH)&(state==LEFT_DIG|state==RIGHT_DIG);

endmodule

44

三. 总结

其实通过这四个题目解答,我们可以观察到我们在解决这个Moore型有限状态机问题时,采用的结构,我们采用独热码进行大致结构总结,用注释进行分析。
如图所示:

module top_module(
    input clk,
    input areset,    
    input bump_left,
    input bump_right,
    output walk_left,
    output walk_right); //  

    parameter         //N位独热码
    reg[N-1:0]      state, next_state; //初态和次态

    always @(*) begin  // This is a combinational always block 组合逻辑
           // 状态的逻辑转换    State transition logic
           ..........
           ..........

    end

    always @(posedge clk or posedge areset) begin // // This is a sequential always block 时序逻辑
        // State flip-flops with asynchronous reset  异步复位状态触发器,本题中都是异步。
        if(areset) state<=..........;//一般为题目默认的状态
        else       state<=next_state;
    end
 /* 同步复位状态触发器
    always @(posedge clk) begin   
        if(reset) state<=..........;
        else      state<=next_state;
    end
    */

    // Output logic 输出
    assign (output)=.......;
    .......................
    .......


endmodule

我在上期文章verilog2康威的生命游戏中,那个做法也是用的同样的有限状态机的结构,可以参考对比一下。Conway’s Game of Life
由于个人水平不足也正在学习,难免出现一些错误,望指正,若有更好的想法也可评论或者私信交流。

  • 38
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值