基于FPGA的“2048”游戏综合设计

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于FPGA的“2048”游戏综合设计相关的知识,希望对你有一定的参考价值。

参考技术A 学号:17020110019    姓名:高少魁

【嵌牛导读】2048游戏在网页端与移动端均可以使用,本设计将该游戏移植到了硬件平台FPGA上,使用Digilent Nexys 4 DDR 开发板,使用Xilinx Vivado 用于搭建基于MicroBlaze 软核处理器的硬件运行环境,“2048”游戏主程序和VGA 显示控制等程序的编写在Xilinx SDK 上进行。游戏必需的上、下、左、右方向控制,在物理上由开发板上的按键实现,在软件层面通过查询方式实现。最终,嵌入式系统的运行结果将输出到显示屏上,包含4×4 棋盘方格、游戏得分和游戏结果。

【嵌牛鼻子】FPGA    VGA    最小嵌入式系统    软核CPU

【嵌牛正文】

一、硬件设计

系统设计的整体电路框图如图所示

硬件部分由基于MicroBlaze 软核处理器的最小嵌入式系统和外围的输入输出模块构成。最小嵌入式系统以MicroBlaze 软核处理器、时钟模块(Clocking Wizard)、处理器系统复位模块(Processor System Reset)、调试模块(MicroBlaze Debug Module,MDM)、内置存储(LocalMemory)和外置存储接口模块(Memory Interface Generator,MIG)为核心,以AXI 总线互联模块(AXI Interconnect)作为处理器与外设的通信基础,以直接或间接挂载于其上的AXI 中断控制器(AXI Interrupt Controller)和中断请求集成器(Concat)作为中断信号的数据通路。

外围输入输出模块包括以下若干部分,它们均通过AXI 总线与处理器通信。UART 模块(AXI Uartlite)实现嵌入式系统与计算机的调试通信,GPIO 模块(AXI GPIO)实现按键输入,TFT 模块(AXI TFT Controller)把系统运行结果通过VGA 接口输出。

由于软件部分需要存储大量的图片资源,且TFT 模块要求显存不少于2MB,而处理器的内置存储空间不足,故使用MIG 模块以使用外置DDR2 存储。硬件系统共需要有三种不同频率的时钟信号:处理器需要100MHz 时钟、MIG 模块需要200MHz 时钟、TFT 模块需要25MHz 时钟,故Clocking Wizard 输出三路时钟。

要把上述嵌入式系统在FPGA 上实现,需要添加管脚约束。共有三处需要添加约束:时钟信号输入、GPIO 模块的按键输入和TFT 模块的VGA 接口输出。前两处在添加相应IP 核时以完成约束,TFT 模块的约束需要手动完成,即把红、绿、蓝三通道颜色信号和行、场同步信号约束到VGA 端子上。

二、软件设计

1.图片素材

嵌入式系统的运行结果,即“2048 ”游戏的状态需要以图形的方式输出到显示屏,因此需要把所有可能显示的较为复杂的内容预先制作好并存储起来。这些内容包括2~ 65536 共16个数字方块、用于显示得分的 0~ 9 这十个数字 、“得分”提示语 和游戏给出的“赢”和“输”的提示信息,均使用 Adobe Photoshop制作。然后,利用 Image 2 Lcd 工具软件把图片输出为 C 语言格式的数组备用。

2.主程序部分

主程序实现的功能为:配置硬件工作参数 、调用 VGA 显示部分的函数来显示两幅静态页面 、初始化游戏、初始化屏幕显示和 识别用户输入的控制信号并传输给游戏核心部分。

硬件工作参数包括GPIO 通道的数据传输方向 、 显存基地址 和 TFT 扫描方式 。 GPIO 用于获取用户输入的控制信息,因此方向为输入 TRI 寄存器的值配置为 0 x1F 。显存基地址为0x 87 E 00000 ,大小为 2MB ,该信息写入 AR 寄存器 。 TFT 显示控制器的工作参数为从左到右扫描、开启显示输出,相应地把 0x 1 写入 CR 寄存器。

程序开始执行时,首先调用VGA 显示部分的两个函数分别 2~ 65536 共 16 种不同数字方格的棋盘。 然后,调用函数开始一局新游戏并在屏幕上预先绘制好不需要更新的棋盘区域底框、“得分”提示信息和显示分数的区域的底框。 主程序使用查询的方式不断获取 GPIO 寄存器提供的用户输入信息。当有输入时,调用游戏核心部分的函数更新“棋盘”信息。

3.VGA显示部分

该部分程序按照一定的规则向显存写入数据以在显示屏上显示不同的内容。这些内容包括 2~ 65536 共 16 种数字方格的棋盘、得分信息、游戏进行时的棋盘和提示信息。

2~ 65536 共 16 种数字方格的棋盘在程序刚开始执行时显示,每幅画面大约显示 5 秒,用软件延时粗略控制。随着游戏的进行, VGA 显示部分不断读取“棋盘”,并把代表相应数字的图片绘制到显示器上。同时,它还读取得分信息,并绘制到预先设定的位置。如果“棋盘”上出现了“ 2048 ”,它输出“赢”的提示信息。如果“棋盘”上的方格均不能移动且没有出现“ 2048 ”,则显示“输”的提示信息。

4.游戏核心部分

游戏核心部分主要涉及对“棋盘” 的处理 ,程序把它抽象为 4 × 4 的二维数组。这部分的功能包括: 游戏初始化、 方块四个方向 的移动和合并策略、向棋盘添加一个新的方块、检查游戏的状态、计算得分和获取游戏的状态信息。

游戏初始化把得分清零,并向棋盘添加两个数字。方块四个方向的移动和合并策略与我们之前体验过的2048 游戏完全一致。方块移动后,“棋盘”上一定会增加一个新的方块,可能是2 或 4 ,概率为 9:1 。之后,程序检查“棋盘”上是否出现过 2048 。如果出现过(“棋盘”上至少有一个不小于“ 2048的方格),则游戏结束,通知 VGA 绘图部分显示“赢”的提示信息。如果没有出现过,则检查棋盘上的空余位置数量数量以以及方块是否还能移动。如果没有空余位置且方块不能继续移动,及方块是否还能移动。如果没有空余位置且方块不能继续移动,则游戏失败,通知则游戏失败,通VGA显示部分显示部分输出“输”的提示信息。接下来,输出“输”的提示信息。接下来,程序将通知程序将通知VGA显示部显示部分更新“棋盘”和得分的显示。分更新“棋盘”和得分的显示。

三、设计运行效果

运行效果如下图

FPGA综合实验 05 - | VGA彩条信号显示控制电路设计

一、实验目的和任务

      学习VGA图像显示控制电路设计。

二、设计代码(或原理图)、仿真波形及分析

1、rom 为图像数据存储模块

// megafunction wizard: %ROM: 1-PORT%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: altsyncram 

// ============================================================
// File Name: rom.v
// Megafunction Name(s):
// 			altsyncram
//
// Simulation Library Files(s):
// 			altera_mf
// ============================================================
// ************************************************************
// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//
// 13.1.0 Build 162 10/23/2013 SJ Full Version
// ************************************************************


//Copyright (C) 1991-2013 Altera Corporation
//Your use of Altera Corporation's design tools, logic functions 
//and other software and tools, and its AMPP partner logic 
//functions, and any output files from any of the foregoing 
//(including device programming or simulation files), and any 
//associated documentation or information are expressly subject 
//to the terms and conditions of the Altera Program License 
//Subscription Agreement, Altera MegaCore Function License 
//Agreement, or other applicable license agreement, including, 
//without limitation, that your use is for the sole purpose of 
//programming logic devices manufactured by Altera and sold by 
//Altera or its authorized distributors.  Please refer to the 
//applicable agreement for further details.


// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module rom (
	address,
	clock,
	q);

	input	[16:0]  address;
	input	  clock;
	output	[23:0]  q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
	tri1	  clock;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif

	wire [23:0] sub_wire0;
	wire [23:0] q = sub_wire0[23:0];

	altsyncram	altsyncram_component (
				.address_a (address),
				.clock0 (clock),
				.q_a (sub_wire0),
				.aclr0 (1'b0),
				.aclr1 (1'b0),
				.address_b (1'b1),
				.addressstall_a (1'b0),
				.addressstall_b (1'b0),
				.byteena_a (1'b1),
				.byteena_b (1'b1),
				.clock1 (1'b1),
				.clocken0 (1'b1),
				.clocken1 (1'b1),
				.clocken2 (1'b1),
				.clocken3 (1'b1),
				.data_a ({24{1'b1}}),
				.data_b (1'b1),
				.eccstatus (),
				.q_b (),
				.rden_a (1'b1),
				.rden_b (1'b1),
				.wren_a (1'b0),
				.wren_b (1'b0));
	defparam
		altsyncram_component.address_aclr_a = "NONE",
		altsyncram_component.clock_enable_input_a = "BYPASS",
		altsyncram_component.clock_enable_output_a = "BYPASS",
		altsyncram_component.init_file = "../timg.mif",
		altsyncram_component.intended_device_family = "Cyclone V",
		altsyncram_component.lpm_hint = "ENABLE_RUNTIME_MOD=NO",
		altsyncram_component.lpm_type = "altsyncram",
		altsyncram_component.numwords_a = 131072,
		altsyncram_component.operation_mode = "ROM",
		altsyncram_component.outdata_aclr_a = "NONE",
		altsyncram_component.outdata_reg_a = "CLOCK0",
		altsyncram_component.widthad_a = 17,
		altsyncram_component.width_a = 24,
		altsyncram_component.width_byteena_a = 1;


endmodule

// ============================================================
// CNX file retrieval info
// ============================================================
// Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0"
// Retrieval info: PRIVATE: AclrAddr NUMERIC "0"
// Retrieval info: PRIVATE: AclrByte NUMERIC "0"
// Retrieval info: PRIVATE: AclrOutput NUMERIC "0"
// Retrieval info: PRIVATE: BYTE_ENABLE NUMERIC "0"
// Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8"
// Retrieval info: PRIVATE: BlankMemory NUMERIC "0"
// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0"
// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0"
// Retrieval info: PRIVATE: Clken NUMERIC "0"
// Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0"
// Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_A"
// Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0"
// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone V"
// Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0"
// Retrieval info: PRIVATE: JTAG_ID STRING "NONE"
// Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0"
// Retrieval info: PRIVATE: MIFfilename STRING "../timg.mif"
// Retrieval info: PRIVATE: NUMWORDS_A NUMERIC "131072"
// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0"
// Retrieval info: PRIVATE: RegAddr NUMERIC "1"
// Retrieval info: PRIVATE: RegOutput NUMERIC "1"
// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
// Retrieval info: PRIVATE: SingleClock NUMERIC "1"
// Retrieval info: PRIVATE: UseDQRAM NUMERIC "0"
// Retrieval info: PRIVATE: WidthAddr NUMERIC "17"
// Retrieval info: PRIVATE: WidthData NUMERIC "24"
// Retrieval info: PRIVATE: rden NUMERIC "0"
// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
// Retrieval info: CONSTANT: ADDRESS_ACLR_A STRING "NONE"
// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS"
// Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS"
// Retrieval info: CONSTANT: INIT_FILE STRING "../timg.mif"
// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone V"
// Retrieval info: CONSTANT: LPM_HINT STRING "ENABLE_RUNTIME_MOD=NO"
// Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram"
// Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "131072"
// Retrieval info: CONSTANT: OPERATION_MODE STRING "ROM"
// Retrieval info: CONSTANT: OUTDATA_ACLR_A STRING "NONE"
// Retrieval info: CONSTANT: OUTDATA_REG_A STRING "CLOCK0"
// Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "17"
// Retrieval info: CONSTANT: WIDTH_A NUMERIC "24"
// Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1"
// Retrieval info: USED_PORT: address 0 0 17 0 INPUT NODEFVAL "address[16..0]"
// Retrieval info: USED_PORT: clock 0 0 0 0 INPUT VCC "clock"
// Retrieval info: USED_PORT: q 0 0 24 0 OUTPUT NODEFVAL "q[23..0]"
// Retrieval info: CONNECT: @address_a 0 0 17 0 address 0 0 17 0
// Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0
// Retrieval info: CONNECT: q 0 0 24 0 @q_a 0 0 24 0
// Retrieval info: GEN_FILE: TYPE_NORMAL rom.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL rom.inc FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL rom.cmp FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL rom.bsf TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL rom_inst.v FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL rom_bb.v TRUE
// Retrieval info: LIB_FILE: altera_mf

2、VGA模块扫描显示功能,代码如下所示

module VGA(clk,hs,vs,rgbout,rgbin,vc,hc,blank,sync);
	input clk;
	input [23:0]rgbin;
	output hs,vs;
	output blank,sync;
	output [23:0]rgbout;
	output[9:0]hc,vc;
	reg [9:0]hcnt,vcnt;
	reg hs,vs;
	reg [9:0]x;
	reg [9:0]y;
	reg [23:0]rgbout;
	assign hc=hcnt,vc=vcnt;
	assign sync=1'b1;
	assign blank =~((hcnt>799)||(vcnt>524));
	
always@(posedge clk)	// 行计数800
	begin 
		if(hcnt==10'd799)
		     hcnt<=10'd0;
		else hcnt<=hcnt+1'b1;
	end
	
always@(posedge clk)	// 场计数525
	begin 
		if(hcnt==10'd639+10'd8)
			begin if(vcnt==10'd524)
				 vcnt<=10'd0;
			else vcnt<=vcnt+1'b1;
		end
	end
	
always@(posedge clk)	// 产生行同步有效信号 
	begin 
		if ((hcnt > (10'd640+10'd8+10'd8)) && (hcnt < (10'd640+10'd8+10'd8+10'd96)))
			hs<=1'b0;
		else
			hs<=1'b1;
	end
	
always@(vcnt)			// 产生场同步有效信号  
	begin
		if ((vcnt > (10'd480+10'd8+10'd2)) && (vcnt < (10'd480+10'd8+10'd2+10'd2)))
			vs<=1'b0;
		else
			vs<=1'b1;
	end
	
always@(posedge clk)	// 送行图像
	begin
		if ((hcnt<10'd640)&&(vcnt<480))
			rgbout=rgbin;
		else 
			rgbout=24'h000000;
	end
	
endmodule

3clock模块实现将输入时钟二分频后输出功能,代码如下所示

module clock(clkin,clkout);
	input clkin;
	output clkout;
	reg clkout;
	always @(posedge clkin) clkout=~clkout;
endmodule

4、顶层文件,上述各模块连接成的原理图,如下所示

5、实验结果截图及原理分析

     原理分析:首先根据VGA图像的扫描时序与实验箱核心板原理图的VGA端口用VerilogHDL语言写出VGA模块,然后新建一个ROM模块把原本已经准备的mif格式的图片加入其中,最后写一个clock模块并生成电路模块进行电路连接。在烧录代码后,FPGA会不断地根据VGA图像的扫描时序来读取图片中的每个像素点RGB,通过时钟信号一直扫描下去,由此能显示出整张图片。

以上是关于基于FPGA的“2048”游戏综合设计的主要内容,如果未能解决你的问题,请参考以下文章

FPGA综合实验 02 - | 正弦信号发生器设计

FPGA综合实验 05 - | VGA彩条信号显示控制电路设计

FPGA综合实验 01 - | 8位加法器的设计

FPGA综合实验 03 - | 8位硬件乘法器设计

FPGA综合实验 04 - | ADC采样控制电路设计

[EDA]FPGA/CPLD 设计流程步骤及步骤概念