一、状态机理论知识
大家知道verilog语言依靠不同的always语句块实现了硬件电路的并行执行,但是在工程中,我们不仅要处理并行执行电路,偶尔也会遇到需要串行执行的电路要求。这时候我们可以选择有限状态机FSM(Finite State Machine)来完美实现。
状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。有限状态机主要分为2大类:
Mealy状态机:时序逻辑的输出不仅取决于当前状态,还与输入有关;
Moore状态机:时序逻辑的输出只与当前状态有关。
根据代码的设计方式 状态机可以分为一段,二段和三段的模式。
一段式
一段式写法就是把所有的逻辑(输入、输出和状态)都放在一个always语句块中,这种后期不容易维护。在简单的状态机可以使用。
二段式
二段式写法就是有两个always block,把时序逻辑和组合逻辑分隔开来。时序逻辑里进行当前状态和下一状态的切换,组合逻辑实现各个输入、输出以及状态判断。这种写法不仅便于阅读、理解、维护,而且利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。在两段式描述中,当前状态的输出用组合逻辑实现,可能存在竞争和冒险,产生毛刺。
要求对状态机的输出用寄存器打一拍,但很多情况不允许插入寄存器节拍,此时使用三段式描述。其优势在于能够根据状态转移规律,在上一状态根据输入条件判断出当前状态的输出,从而不需要额外插入时钟节拍。
三段式
三段式写法就是有三个always block,一个时序逻辑采用同步时序的方式描述状态转移,一个采用组合逻辑的方式判断状态转移条件、描述状态转移规律,第三个模块使用同步时序的方式描述每个状态的输出。代码容易维护,时序逻辑的输出解决了两段式组合逻辑的毛刺问题,但是从资源消耗的角度上看,三段式的资源消耗多一些。
二、游戏里的状态机
1. 游戏状态控制之状态机(一段式)
状态机框图
我们用状态机来完成游戏的控制过程:
关于这四个状态的描述请参考友晶科技FPGA开发板实现贪吃蛇游戏(一)——整体描述 DE10-STANDARD、DE1-SOC、DE2-115 状态机设计
设计问题点提醒
这个cnt_clk计数器这里不能写成cnt_clk=150000000,因为第6s的时间只有一瞬间,当这个时候没有拨动SW时,其他时刻它就不再去判断SW的情况了。
当游戏结束时,按任意键触发游戏重新开始,此时需要将这两个计数器清零,不然重新开始的时候会跳过欢迎界面,以及再次游戏结束时蛇不会闪烁。
其实就是要确保在重启状态下,所有的条件要回到跟复位时一样。
游戏控制单元(game_ctrl_unit.v)的源代码:
//游戏控制模块 根据游戏状态产生相应控制信号 module game_ctrl_unit ( input clk,//25MHz input rst_n,//系统复位 input key0_right,//方向控制按钮,控制向左移动 input key1_left,//方向控制按钮,控制向右移动 input key2_down,//方向控制按钮,控制向上移动 input key3_up,//方向控制按钮,控制向下移动 input [2:0]sw,//完成难度选择的操作 input hit_wall,//撞墙标志 input hit_body,//撞自身标志 input [11:0]bcd_data,//分数累计到100则游戏成功 output reg snake_display,//蛇整体显示标志 output reg [1:0]game_status//当前游戏状态 ); //游戏分四个状态 localparam RESTART = 2'b00; //游戏重启 localparam START = 2'b01; //游戏开始 localparam PLAY = 2'b10; //游戏进行 localparam DIE = 2'b11; //游戏结束 reg [32:0]cnt_clk; reg[31:0]flash_cnt;//蛇闪烁时间计数器 //状态机定义初始状态,并描述状态转移与输出 always@(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt_clk<=0; flash_cnt<=0; snake_display<=1; game_status <= RESTART; //复位后游戏状态进入重启状态 end else begin case(game_status) RESTART:begin //游戏重启状态 cnt_clk<=cnt_clk+1; if(cnt_clk>150000000)begin// "欢迎来到贪吃蛇游戏“ 界面显示需要6s时间 if(sw[0]||sw[1]||sw[2]) begin game_status <= START;//选择游戏难度后进入START状态 end end else begin game_status <= RESTART; end end START:begin if ((~key0_right) ||( ~key1_left) || (~key2_down) ||( ~key3_up))//四个按键有任意一个按键被按下即可开始游戏 game_status <= PLAY; else game_status <= START; end PLAY:begin if(hit_wall || hit_body||bcd_data[11:8]>=1'd1)//如果撞墙或者撞自身则 游戏结束 game_status <= DIE; else game_status <= PLAY; end //下面代码是在制造闪烁效果 //snake_display信号初始化的时候为高 //snake_display信号在0-0.5秒为高,在 0.5-1秒为低,在 1-1.5秒高 在1.5-2低 2-2.5秒高 在2.5-3秒低 DIE:begin if(flash_cnt <= 100_000_000) begin//flash_cnt计时4秒 flash_cnt <= flash_cnt + 1'b1; if(flash_cnt == 12_500_000)begin//0-0.5秒 高 snake_display <= 1'b0;end else if(flash_cnt == 25_000_000)begin//0.5-1秒低 snake_display <= 1'b1;end else if(flash_cnt == 37_500_000)begin//1-1.5秒高 snake_display <= 1'b0;end else if(flash_cnt == 50_000_000)begin//1.5-2秒低 snake_display <= 1'b1;end else if(flash_cnt == 62_500_000)begin//2-2.5秒高 snake_display <= 1'b0;end else if(flash_cnt == 75_000_000)begin//2.5-3秒低 snake_display <= 1'b1; end end //游戏结束后按任意按键重新开始 else if((~key0_right) ||( ~key1_left) || (~key2_down) ||( ~key3_up) ) begin cnt_clk<=0; flash_cnt<=0; game_status <= RESTART; end else begin game_status <= DIE; end end default:begin game_status <= RESTART; //游状态 从游戏结束 到游戏重启 end endcase end end endmodule
2. 蛇身方向控制之状态机(三段式)
蛇身方向控制可以用一个状态机来实现。四个状态:上下左右。
这个状态机的实现是三段式。
状态机第一段
状态机第一段:完成初始化状态,以及完成当前状态转换到下一状态的时序逻辑:
首先方向一定要初始化一下,不初始化的话方向就会错乱, 虽然这里初始化方向是向右,但实际上运行时起步方向的判断是根据key来判断。
状态机第二段
状态机第二段:在当前状态在当前输出下转换为下一状态的组合逻辑。
根据按键进行三个方向的选择,这里是按键按下的时候,信号传导Direct_next,然后由Direct_next送给Direct_r:
状态机第三段
状态机第三段:当前状态(和当前输入)产生当前输出的时序逻辑:
当方向向上且不撞到墙壁时,蛇头的y轴坐标+1, 也就是蛇向上移动一格。
当方向向下且不撞到墙壁时,蛇头的y轴坐标-1, 也就是蛇向下移动一格。
当方向向左且不撞到墙壁时,蛇头的x轴坐标-1, 也就是蛇向左移动一格。
当方向向上且不撞到墙壁时,蛇头的x轴坐标+1, 也就是蛇向右移动一格。
3.相关阅读:
友晶科技FPGA开发板实现贪吃蛇游戏(九)——蛇身控制 DE10-STANDARD、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板实现贪吃蛇游戏(八)——游戏控制之状态机设计 DE10-STANDARD、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板实现贪吃蛇游戏(七)食物(苹果)的产生 DE10-STANDARD、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板实现贪吃蛇游戏(六)VGA 驱动模块图片显示 DE10-STANDARD、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板实现贪吃蛇游戏(五)VGA 驱动模块字符显示 DE10-STANDARD、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板实现贪吃蛇游戏(四)VGA 驱动模块色块显示 DE10-STANDARD、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板实现贪吃蛇游戏(三)记分模块 DE10-STANDARD、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板实现贪吃蛇游戏(二)数码管驱动模块 DE10-STANDARD、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)
友晶科技FPGA开发板实现贪吃蛇游戏(一)——整体描述 DE10-Standard、DE1-SOC、DE2-115 - Doreen的FPGA自留地 - 博客园 (cnblogs.com)