基于FPGA的乒乓球游戏机设计综述


摘要
在现代电子设计领域,微电子技术迅猛发展,无论是系统设计、电路设计,还是芯片设计, 其设计的复杂度都在增加, 传统的手工设计方法已经不能满足设计者的要求, 急需新的设计 工具来解决。EDA 技术的迅速发展顺应了时代的发展,使得电子设计自动化技术有了新的、 快的发展,其重要程度日益突出。本文设计了基于 FPGA 的,用 Verilog HDL 语言描述的乒乓 球游戏机的设计。它由控制模块、数码管的片选信号模块、送数据模块和 7 段译码器模块组 成,连接形成乒乓球游戏机的顶层电路。在 QuartusⅡ软件上用 Verilog HDL 语言分别对每个 模块进行描述,然后在软件上进行编译、仿真,最终实现乓乓球比赛的基本过程和规则,并 能自动裁判和计分,达到设计的要求。 关键词:EDA ;FPGA ;Verilog HDL Abstract In the field of modern electronic design, the rapid development of microelectronic technology,whether the system design, circuit design and chip design, the design is more complex, the traditional manual design methods have been unable to meet the requirements of designers,in urgent need of new design tools to solve. The rapid development of EDA technology to the development of the times, the electronic design automation technology has been the development of new, fast, its importance is outstanding day by day. This paper is designed based on FPGA, the design of table tennis game is described with Verilog HDL language. It consists of control module, chip select signal module, digital tube to send data module and the 7 segment decoder modules, the top-level circuit connected to form a table tennis game. In the Quartus software by Verilog HDL language for each module are described, and then the compiler, simulation in software, realize the basic process and the rules of table tennis table tennis competition, and can automatically judge and score, to meet the design requirements. Keywords: EDA; FPGA; Verilog HDL

第一章
1.1 课题研究的背景

引言

随着微电子技术的飞跃发展,无论是系统级设计、电路设计还是芯片设计, 它的复杂程度都在不断的增加,而且它的发展速度也越来越快。这时,仅仅依靠 传统的电子设计方法已经不能满足需求。 EDA 技术的兴起与发展给电子设计带来 了革命性的变化,推动了微电子技术的迅猛发展,电子学进入一个崭新的时代。 1.2 课题研究的目的 EDA 技术是现代电子设计领域的一门技术, 它提供了基于计算机和信息技术 的电路设计方法,不依托其他设计工具,仅以计算机为工具,在 EDA 软件上完 成设计、编译、仿真。EDA 技术正以空前的发展速度和规模渗透到各行各业。 在这个快节奏生活的社会, 人们外出的休闲娱乐活动越来少,而越来越多的 电子游戏解决了这个问题, 人们可以在家中玩各种休闲娱乐节目。所以设计了基 于 FPGA 的两人乒乓球游戏机, 让人们能在忙碌中有时间体验下体育节目的乐趣。 FPGA 器件具有高开发周期短,高集成度,现场可修改等特点,因此十分有必要 对 FPGA 进行详细认真的研究。硬件描述语言 VHDL 功能性强,灵活性高,覆盖 面广,用以甚高速集成电路硬件描述语言,具有很好的实用性。总的来说,现代 EDA 技术的基本特征是采用高级语言(VHDL、Verilog HDL 等)描述,具有系统 级仿真和综合的能力。它必将成为 21 世纪不可或缺的电子设计工具,必将影响 这个世纪的发展。

2.1EDA 基础 2.1.1EDA 定义 EDA 是 Electronics Design Automation(电子设计自动化)的缩写,以计算机 为工作平台;在 20 世纪 90 年代初从计算机辅助设计(CAD) 、计算机辅助制造 (CAM) 、计算机辅助测试(CAT)和计算机辅助工程(CAE)的定义发展而来的; 是利用电子技术基础、 计算机技术、智能化技术等多种应用技术而开发成的整套 电子 CAD 软件;是一种帮助从事电子元件产品和系统设计的电子工作者的综合 技术。

2.1.2EDA 技术的发展历程 集成电路的飞快发展不断给 EDA 技术提出更高的要求,对 EDA 技术的发展 起了巨大的推动作用。 从 20 世纪 60 年代中期开始, 人们不断地开发出各种计算 机辅助设计工具来帮助设计人员进行集成电路和电子系统的设计。 经历了计算机 辅助设计、计算机辅助工程设计和电子系统设计自动化这 3 个阶段,如图 2.1 所 示。

行为

行为综合

功能 20 世纪 90 年代 电子设计自动化 逻辑综合 逻辑 20 世纪 80 年代 计算机辅助工程 布局布线 版图

图形生成 20 世纪 70 年代 计算机辅助设计

掩膜

图 2.1 EDA 技术的发展历程

(1)CAD 阶段 20 世纪 70 年代,随着中、小规模集成电路的兴起和应用,传统的手工设计 印刷电路板和集成电路的方法已经不能满足设计精度和效率的要求, 于是电子设 计工程师们开始在二维平面图形上进行计算机辅助设计,这样就产生了第一代

EDA 工具,设计者从繁杂、机械的、手工布局和布线工作中解放了出来。 (2)CAE 阶段 20 世纪 80 年代以后,集成电路的规模越来越大,电子系统设计的复杂也在 增加,电子设计自动化的工具逐步完善和发展起来,设计者们尤其在设计方法、 设计工具集成化方面取得了很大的进步。 为了适应电子产品在规模和制作上的需 要,以计算机仿真和自动布线为核心的第二代 EDA 技术应运而生。其特点是以 软件设计工具为核心,通过软件完成产品的开发、设计、分析、生产和测试等工 作。 (3)ESDA 阶段 20 世纪 90 年代,设计师们逐步从使用硬件转向去设计硬件,从单个电子产 品开发转向系统级电子产品开发(及片上系统集成) 。因此,这时的 EDA 工具是 以系统级设计为核心,包括结构综合与系统行为级描述,系统仿真与测试验证, 系统决策与文件生成, 系统划分与指标分配等一整套的电子系统设计工具。这时 的 EDA 工具不仅具有电子系统设计的能力,而且具有独立于生产工艺和厂家的 系统级设计能力。第三代 EDA 工具的出现,极大地提高了电子系统设计的效率, 让设计工程师们开始能够以概念来驱动设计工程的梦想。 2.1.3EDA 技术的发展趋势 随着微电子技术的不断发展与进步和市场需求的不断增长,EDA 技术在 21 世纪后得到了迅猛的发展,这一发展趋势表现在以下几个方面: (1)软件 IP 核在微电子的产业领域、设计应用领域和技术领域得到进一步的巩 固与发展。 (2)使支持硬件描述语言仿真和设计的 EDA 软件不断地强大起来。 (3)EDA 使得系统与器件、专用集成电路 ASIC 与 FPGA、模拟与数字、行为与 结构、软件与硬件等的界限越来越模糊。 (4) EDA 技术保护了设计者的电子设计成果的知识产权。 (5)大规模的电子系统都是以 EDA 为工具设计的,正以飞快的速度渗透到 IP 核 模块。 (6)更大规模的可编程逻辑器件正在推向市场,使得设计更为广泛与便捷。

2.1.4EDA 技术的基本特征 总的来说,现代 EDA 技术是采用高级程序语言描述,具有系统级仿真和综 合的能力。它主要采用并行工程和“自顶向下”的设计方法,使开发人员从一开 始就能考虑到产品生成周期的诸多方面,包括质量、成本、开发时间及用户的需 求等。然后从系统设计开始,在顶层进行功能方框图的划分和结构的设计,在方 框图一级进行仿真、纠错,并用 VHDL、Verilog-HDL 等硬件描述语言对顶层的系 统进行功能和行为上的描述,在系统一级进行验证与仿真。最后,用逻辑综合优 化工具生成具体的门级逻辑电路的网表, 其对应的物理实现级可以是印刷电路板 或者是专用的集成电路。 近年来,硬件描述语言等设计数据格式逐步形成一种标 准,不同的设计风格和应用要求使得各具特色的 EDA 工具被集成在相同的设计 方法上,EDA 技术的设计框架日趋标准化。 2.1.5EDA 技术的基本工具 集成电路技术的不断发展对 EDA 技术提出了更高的要求,促进了 EDA 技术 向更高的层次发展。但实际来说,EDA 系统的设计能力一直难以满足集成电路的 要求。EDA 工具的发展经历了两个阶段,即物理工具阶段和逻辑工具阶段。 现在, 人们已经开发了很多计算机辅助设计工具来帮助设计集成电路,常见 的 EDA 工具有编辑器、仿真器、检查/分析工具和优化/综合工具等,如图 2.2 所 示。

EDA 设计工具

检查/分析工具

编辑器

仿真器

优化/综合工具

文字编辑器

图形编辑器

统计型编辑器

确定型仿真器

图 2.2 EDA 设计工具的分类

2.1.6EDA 技术的基本设计思路 (1)EDA 技术的电路级设计 电路级设计工作,首先,应该先确定合适的设计方案,然后选择方便实现该 方案的元器件, 接着就可根据所选元器件设计符合要求的电路原理图,再接着进 行一次仿真。其目的是检验此设计方案在元件模型库支持下的功能方面是否正 确,这样,设计工作就进入了轨道。第一次仿真通过后,就开始进行 PCB 的自动 布局布线,此布局布线要根据原理图产生的电路连接网络表来进行。然后,对 PCB 进行分析,将分析结果反馈给电路图,并再次仿真。这样,PCB 板在实际工 作中的可行性就一目了然了。综上,EDA 的电路级设计可在实际的电子系统产生 之前就了解其功能特性, 从而降低其设计风险, 降低开发成本, 缩短其开发周期, 使得设计人员能够更好、更方便的设计。电路级设计工作流程如图 2.3

方案设计

元件符号库

原理图设计

元件模型库

系统仿真

自动布局

PCB 后仿真

制造 PCB

系统实现

图 2.3 电路级设计工作流程图

(2)EDA 技术的系统级设计 EDA 技术的系统级设计方法是采用“自顶向下”的思路来设计的,让开发者 一开始就可了解到产品的开发周期、生产成本等。设计者首先从系统方案入手,

进行顶层的划分和结构设计;然后,用 VHDL 语言等硬件描述语言对系统进行深 刻描述;接着就用编辑器将其转换成标准的 VHDL 文件,再接着验证系统功能设 计的正确性; 再接着就用逻辑综合优化工具生成具体的门级电路的网络表; 其后, 进行时序仿真;最后,就到了系统的物理实现级,将其变成 FPGA 等。EDA 技术 的系统级设计如图 2.4 所示。

方案设计

综合器

系统规划

时序仿真

VHDL 代 码 输 入 编译器

适配器

编程文件

功能仿真

FPGA 等

图 2.4 EDA 技术的系统级设计

2.1.7 EDA 的设计流程 EDA 的设计流程是“自顶向下”的设计思路。设计流程如图 2.5 所示。

原理图/HDL 文本编辑 功能仿真 综合 FPGA/CPLD 器件 和电路系统 逻辑综合器 FPGA/CPLD 适配 结构综合器 FPGA/CPLD 编程下载 时 序与 功能 门级仿真 1.功能仿真 2.时序仿真

1. 2. 3. 4.

ISP 方式下载 JTAG 方式下载 针对 SRAM 结构的配置 OTP 器件编程

图 2.5 应用于 FPGA/CPLD 的 EDA 开发流程

2.2 FPGA 的概念与特点
FPGA(Field-Programmable Gate Array) ,即现场可编程门阵列,它是在 PAL、GAL、CPLD 等可编程器件的基础上进一步发展的产物。它是作为专用集成 电路(ASIC)领域中的一种半定制电路而出现的,既解决了定制电路的不足, 又克服了原有可编程器件门电路数有限的缺点。 目前以硬件描述语言( Verilog 或 VHDL )所完成的电路设计,可以经 过简单的综合与布局,快速的烧录至 FPGA 上进行测试,是现代 IC 设计 验证的技术主流。这些可编辑元件可以被用来实现一些基本的逻辑门电路 (比如 AND 、OR、XOR 、NOT )或者更复杂一些的组合功能比如解码器或数学 方程式。在大多数的 FPGA 里面,这些可编辑的元件里也包含记忆元件例如 触发器( Flip - flop )或者其他更加完整的记忆块。 系统设计师可以根据需要通过可编辑的连接把 FPGA 内部的逻辑块连接 起来, 就好像一个电路试验板被放在了一个芯片里。 一个出厂后的成品 FPGA 的逻辑块和连接可以按照设计者而改变,所以 FPGA 可以完成所需要的逻辑 功能。 FPGA 一般来说比 ASIC(专用集成芯片)的速度要慢,无法完成复杂的 设计,而且消耗更多的电能。但是他们也有很多的优点比如可以快速成品, 可以被修改来改正程序中的错误和更便宜的造价。厂商也可能会提供便宜 的但是编辑能力差的 FPGA 。因为这些芯片有比较差的可编辑能力,所以这 些设计的开发是在普通的 FPGA 上完成的,然后将设计转移到一个类似于 ASIC 的芯片上。另外一种方法是用 CPLD (复杂可编程逻辑器件备)。 早在 1980 年代中期, FPGA 已经在 PLD 设备中扎根。 CPLD 和 FPGA 包括 了一些相对大数量的可以编辑逻辑单元。 CPLD 逻辑门的密度在几千到几万 个逻辑单元之间,而 FPGA 通常是在几万到几百万。 CPLD 和 FPGA 的主要区别是他们的系统结构。 CPLD 是一个有点限制性 的结构。这个结构由一个或者多个可编辑的结果之和的逻辑组列和一些相 对少量的锁定的寄存器。这样的结果是缺乏编辑灵活性,但是却有可以预 计的延迟时间和逻辑单元对连接单元高比率的优点。而 FPGA 却是有很多的 连接单元,这样虽然让它可以更加灵活的编辑,但是结构却复杂的多。

CPLD 和 FPGA 另外一个区别是大多数的 FPGA 含有高层次的内置模块 (比 如加法器和乘法器)和内置的记忆体。一个因此有关的重要区别是很多新 的 FPGA 支持完全的或者部分的系统内重新配置。 允许他们的设计随着系统 升级或者动态重新配置而改变。 一些 FPGA 可以让设备的一部分重新编辑而 其他部分继续正常运行。 FPGA 采用了逻辑单元阵列 LCA ( Logic Cell Array )这样一个概念, 内部包括可配置逻辑模块 CLB ( Configurable Logic Block )、输出输入模 块 IOB( Input Output Block )和内部连线( Interconnect )三个部分。FPGA 的基本特点主要有: 1 )采用 FPGA 设计 ASIC 电路,用户不需要投片生产,就能得到合用的 芯片。 2 ) FPGA 可做其它全定制或半定制 ASIC 电路的中试样片。 3 ) FPGA 内部有丰富的触发器和 I / O 引脚。 4 ) FPGA 是 ASIC 电路中设计周期最短、开发费用最低、风险最小的器 件之一。 5) FPGA 采用高速 CHMOS 工艺,功耗低,可以与 CMOS 、 TTL 电平兼容。 可以说, FPGA 芯片是小批量系统提高系统集成度、可靠性的最佳选择 之一。 FPGA 是由存放在片内 RAM 中的程序来设置其工作状态的,因此,工作 时需要对片内的 RAM 进行编程。用户可以根据不同的配置模式,采用不同 的编程方式。加电时, FPGA 芯片将 EPROM 中数据读入片内编程 RAM 中,配 置完成后, FPGA 进入工作状态。掉电后, FPGA 恢复成白片,内部逻辑关系 消失,因此, FPGA 能够反复使用。 FPGA 的编程无须专用的 FPGA 编程器, 只须用通用的 EPROM 、 PROM 编程器即可。当需要修改 FPGA 功能时,只需换 一片 EPROM 即可。这样,同一片 FPGA ,不同的编程数据,可以产生不同的 电路功能。因此, FPGA 的使用非常灵活。 Verilog HDL 语言简介 Verilog HDL 和 VHDL 是目前世界上最流行的两种硬件描述语言 (HDL: Hardware Description Language) ,均为 IEEE 标准,被广泛地应用于基于可编程逻辑器件 的项目开发。 二者都是在 20 世纪 80 年代中期开发出来的, 前者由 Gateway Design Automation 公司(该公司于 1989 年被 Cadence 公司收购)开发,后者由美国军 方研发。 HDL 语言以文本形式来描述数字系统硬件结构和行为,是一种用形式化方法来 描述数字电路和系统的语言, 可以从上层到下层来逐层描述自己的设计思想。即 用一系列分层次的模块来表示复杂的数字系统,并逐层进行验证仿真,再把具体

的模块组合由综合工具转化成门级网表, 接下去再利用布局布线工具把网表转化 为具体电路结构的实现。目前,这种自顶向下的方法已被广泛使用。概括地讲, HDL 语言包含以下主要特征: Verilog HDL 语言既包含一些高级程序设计语言的结构形式,同时也兼顾 描述硬件线路连接的具体结构。 通过使用结构级行为描述,可以在不同的抽象层次描述设计。Verilog HDL 语 言采用自顶向下的数字电路设计方法,主要包括 3 个领域 5 个抽象层次。 Verilog HDL 语言是并行处理的,具有同一时刻执行多任务的能力。这和一 般高级设计语言(例如 C 语言等)串行执行的特征是不同的。 Verilog HDL 语言具有时序的概念。一般的高级编程语言是没有时序概念的, 但在硬件电路中从输入到输出总是有延时存在的,为了描述这一特征,需要引入 时延的概念。HDL 语言不仅可以描述硬件电路的功能,还可以描述电路的时序。 2.1.1 Verilog HDL 语言的历史 1983 年,Gateway Design Automation(GDA) 硬件描述语言公司的 Philip Moorby 首创了 Verilog HDL。后来 Moorby 成为 Verilog HDL-XL 的主要设计者和 Cadence 公司的第一合伙人。1984 至 1986 年, Moorby 设计出第一个关于 Verilog HDL 的仿真器,并提出了用于快速门级仿真 的 XL 算法,使 Verilog HDL 语言得到迅速发展。1987 年 Synonsys 公司开始使 用 Verilog HDL 行为语言作为综合工具的输入。1989 年 Cadence 公司收购了 Gateway 公司,Verilog HDL 成为 Cadence 公司的私有财产。1990 年初,Cadence 公司把 Verilog HDL 和 Verilog HDL-XL 分开,并公开发布了 Verilog HDL。随 后成立的 OVI(Open Verilog HDL International)组织负责 Verilog HDL 的发 展并制定有关标准,OVI 由 Verilog HDL 的使用者和 CAE 供应商组成。1993 年, 几乎所有 ASIC 厂商都开始支持 Verilog HDL,并且认为 Verilog HDL-XL 是最好 的仿真器。 同时, OVI 推出 2.0 版本的 Verilong HDL 规范, IEEE 则将 OVI 的 Verilog HDL2.0 作为 IEEE 标准的提案。1995 年 12 月,IEEE 制定了 Verilog HDL 的标准 IEEE1364-1995。 目前, 最新的 Verilog 语言版本是 2000 年 IEEE 公布的 Verilog 2001 标准,其大幅度地提高了系统级和可综合性能。

Verilog

HDL 的主要能力

Verilog HDL 既是一种行为描述语言,也是一种结构描述语言。如果按照一定 的规则和风格编写代码, 就可以将功能行为模块通过工具自动转化为门级互连的 结构模块。 这意味着利用 Verilog 语言所提供的功能,就可以构造一个模块间的 清晰结构来描述复杂的大型设计,并对所需的逻辑电路进行严格的设计。 下面 列出的是 Verilog 语言的主要功能:

1.可描述顺序执行或并行执行的程序结构; 2.用延迟表示式或事件表达式来明确地控制过程的启动时间; 3.通过命名的事件来触发其他过程里的激活行为或停止行为; 4.提供了条件和循环等程序结构; 5.提供了可带参数且非零延续时间的任务程序结构; 6.提供了可定义新的操作符的函数结构; 7.提供了用于建立表达式的算术运算符、逻辑运算符和位运算符; 8.提供了一套完整的表示组合逻辑基本元件的原语; 9.提供了双向通路和电阻器件的描述; 10.可建立 MOS 器件的电荷分享和衰减模型; 11.可以通过构造性语句精确地建立信号模型; 此外, Verilog HDL 语言还有一个重要特征就是: 和 C 语言风格有很多的相似之处, 学习起来比较容易。

Verilog HDL 和 VHDL 的区别 Verilog HDL 和 VHDL 都是用于逻辑设计的硬件描述语言。 VHDL 在 1987 年成为 IEEE 标准,Verilog HDL 则在 1995 年才成为 IEEE 标准,这是因为前者是美国军方组织 开发的,而后者则是从民间公司转化而来,要成为国际标准就必须放弃专利。相 比而言,Verilog HDL 具有更强的生命力。 Verilog HDL 和 VHDL 的相同点在于:都能形式化地抽象表示电路的行为和结构; 支持逻辑设计中层次与范围的描述;可以简化电路行为的描述;具有电路仿真和 验证机制;支持电路描述由高层到低层的综合转换;与实现工艺无关;便于管理 和设计重用。 但 Verilog HDL 和 VHDL 又有各自的特点,由于 Verilog HDL 推出较早,因而拥有 更广泛的客户群体、更丰富的资源。Verilog HDL 还有一个优点就是容易掌握,如 果具有 C 语言学习的基础,很快就能够掌握。而 VHDL 需要 Ada 编程语言基础, 一般需要半年以上的专业培训才能够掌握。传统观点认为 Verilog HDL 在系统级 抽象方面较弱,不太适合特大型的系统。但经过 Verilog 2001 标准的补充之后, 系统级表述性能和可综合性能有了大幅度提高。当然,这两种语言也仍处于不断 完善的过程中,都在朝着更高级描述语言的方向前进。 Verilog HDL 设计方法 1. 自下而上的设计方法 自下而上的设计是传统的设计方法,是从基本单元出发,对设计进行逐层划分的 过程。 这种设计方法与用电子元件在模拟实现板上建立一个系统的步骤有密切的 关系。优、缺点分别如下:

优点 : 设计人员对这种设计方法比较熟悉;实现各个子模块所需的时间较短。 缺点 : 对系统的整体功能把握不足; 由于必须先对多个子模块进行设计,因此实现整个 系统的功能所需的时间长;另外,对设计人员之间相互协作也有较高的要求。 2.自上而下的设计方法 自上而下的设计是从系统级开始,把系统划分为基本单元,然后再把基本单元划 分为下一层次的基本单元,直到可用 EDA 元件实现为止。这种方法的优、缺点 如下。 优点: 在设计周期开始就做好了系统分析; 由于设计的主要仿真和调试过程是在高层完 成的,所以能够早期发现结构设计上的错误,避免了设计工作的浪费,方便了系 统的划分和整个项目的管理,可减少设计人员劳动,避免了重复设计。 缺点 : 得到的最小单元不标准,且制造成本高。 3.混合的设计方法 复杂数字逻辑电路和系统设计过程,通常是以上两种设计方法的结合。设计时需 要考虑多个目标的综合平衡。 在高层系统用自上而下的设计方法实现,而使用自 下而上的方法从库元件或以往设计库中调用已有的设计单元。 混合设计方法兼有 以上两种方法的优点,并且可使用先进的矢量测试方法。

第二章 设计思想 1.基本原理 用 8 个(或更多个)LED 排成一条直线,以中点为界,两边各代表参赛双方的 位置,其中一只点亮的 LED 指示球的当前位置,点亮的 LED 依此从左到右,或从 右到左,其移动的速度应能调节。当“球”(点亮的那只 LED)运动到某方的最后 一位时, 参赛者应能果断地按下位于自己一方的按钮开关, 即表示启动球拍击球。 若击中,则球向相反方向移动;若未击中,则对方得 1 分。一方得分时,电路自 动响铃 3 秒, 这期间发球无效, 等铃声停止后方能继续比赛。 设置自动记分电路, 甲、乙双方各用 2 位数码管进行记分显示,每计满 21 分为 1 局。甲、乙双方各 设一个发光二极管,表示拥有发球权,每隔 5 次自动交换发球权,拥有发球权的 一方发球才有效。根据乒乓球比赛的过程和规则,首先游戏开始,如果一方非正 确击球则另一方加分,当分数大于 21 时获胜,游戏结束,我们把设计流程规定 如图 1.1 所示。状态机设置了 7 个状态,分别是“等待发球状态” , “第一盏灯亮 状态” , “第八盏灯亮状态” , “球向乙移动状态” , “ 球向甲移动状态” , “允许甲 击球状态” , “允许乙击球状态” 。这是该程序中起决定作用的七个状态。开始的 时候处于“等待发球状态” ,若甲发球则状态转移到“第一盏灯亮状态” ,若乙发 球则转移到“第八盏灯亮状态” ,具体说明以甲球为例。 若发球后乙没有提前击球——规定球移动到对方第一个发光二极管时允许 击球,那么状态机从“第一盏灯亮状态”转移到“球向乙移动状态” 。若在“球 向乙移动状态”乙仍然没有提前击球,状态就转移到“允许乙击球状态” ,在此 状态下,如果乙击球了,那么状态就转移到“ 球向甲移动状态” 。在“第一盏灯

亮状态” , “球向乙移动状态”中,如果乙击球了 ,就算提前击球,这样甲得 分,状态转移到“等待发球状态”等待发球, “ 球向甲移动状态”之后的过程和 前面的过程只不过是甲乙角色的调换而已。 2.设计框图

图 1 流程图

图 2 基本原理

第三章 设计步骤和调试过程 1、模块设计和相应模块代码 (1)发球选择模块的设计 游戏开始时,必须先决定发球权在哪一方。同时,在游戏进行的过程中,必须 能够正确交换甲乙双方发球权。相应的代码如下: module Permissions(clk,res,in1,in2,out1,out2,en_jia,en_yi); input clk,res,in1,in2; output out1,out2,en_jia,en_yi; reg out1,out2,en_jia,en_yi; wire w1,w2,a,b; or add4 select u2(w1,in1,in2); u1(.res(res),.c(w1),.cnt(w2)); u3(.cnt(w2),.a(a),.b(b)); s0=1'b00,s1=1'b01; current_state,next_current;

parameter[1:0] reg[1:0]

always@(posedge clk or negedge res) begin if(!res) current_state<=s0; else current_state<=next_current; end always@(current_state or next_current) begin case(current_state) s0: begin out1<=1; en_jia<=1; en_yi<=0; out2<=0; if(a==1) next_current<=s1; else next_current<=s0; end s1: begin out1<=0;

//复位

en_jia<=0; en_yi<=1; out2<=1; if(b==1) next_current<=s0; else next_current<=s1; end endcase end endmodule

(2)状态机编程实现 状态机设置了 7 个状态,它们代表的具体数值依次是 0 到 6。在波形模拟图中 是用数值来表示状态的。 在整个程序中,状态机起的是中央控制器的作用,由它控制的信号来影响整 个程序中的其他相关部分,如记分部分,发光二极管部分。乒乓球游戏机中有两 个计数器, 分别记忆甲和乙的得分, 用发光二极管的轮流发光表示球的移动轨迹。 状态机的进程如下: module state_machine (clk,res,key1,key2,led,jia,yi,en_jia, en_yi,win,t1,t2); input clk,res,key1,key2,en_jia,en_yi,win,t1; output jia,yi,t2; reg jia,yi,t2; output[7:0] led; reg[7:0] led; parameter[7:0] s0=8'b0000_0001,s1=8'b0000_0010,s2=8'b0000_0100,s3=8'b0000_1000, s4=8'b0001_0000,s5=8'b0010_0000,s6=8'b0100_0000,s7=8'b1000_0000; reg[7:0] current_state,next_current; /*--------------------每个时钟沿转跳一次逻辑状态 --------------------*/ always@(posedge clk or negedge res) begin if(~res) current_state<=s0; else current_state<=next_current; end

/*--------------------产生下一状态的组合逻辑 -----------------------*/ always@(current_state) begin case(current_state) s0: if((key1==1)&&(en_jia==1)) next_current<=s1; else if((key2==1)&&(en_yi==1)) next_current<=s2; else next_current<=s0; s1: if(led==8'b0000_0001) next_current<=s3; else next_current<=s1; s2: if(led==8'b1000_0000) next_current<=s4; else next_current<=s2; s3: if((t1==1)&&(key2==1)) next_current<=s2; else next_current<=s5; s4: if((t1==1)&&(key1==1)) next_current<=s1; else next_current<=s6; s5: if(win==1) next_current<=s7; else if((en_jia==1)||(en_yi==1)) next_current<=s0; else next_current<=s5; s6: if(win==1) next_current<=s7; else if((en_jia==1)||(en_yi==1)) next_current<=s0; else next_current<=s6; endcase end /*--------------------状态机输出的组合逻辑 -------------------------*/ always@(posedge clk or negedge res) begin if(~res) begin led<=8'b0000_0000; t2<=0; jia<=0;

yi<=0; end else begin case(current_state) s0: led<=8'b0000_0000; s1: begin led=8'b1000_0000; #1000 led=8'b0100_0000; #1000 led=8'b0010_0000; #1000 led=8'b0001_0000; #1000 led=8'b0000_1000; #1000 led=8'b0000_0100; #1000 led=8'b0000_0010; #1000 led=8'b0000_0001; end s2: begin led=8'b0000_0001; #1000 led=8'b0000_0010; #1000 led=8'b0000_0100; #1000 led=8'b0000_1000; #1000 led=8'b0001_0000; #1000 led=8'b0010_0000; #1000 led=8'b0100_0000; #1000 led=8'b1000_0000; end s3: begin led<=8'b0000_0001; t2<=1; end s4: begin led<=8'b1000_0000; t2<=1; end s5: jia<=1; s6: yi<=1; s7:

begin #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 end endcase end end endmodule (3)得分判断模块的设计 在一方并没有击球成功时, 需要进行判定,并且鸣响蜂鸣器来提示这个球某一 方没有击中,而另一方得分。于是编写相应程序。 module goal(res,buzz,jia,yi,en); input res,jia,yi; output buzz,en; reg buzz,en; reg[12:0] q; wire w; or u1(w,jia,yi); led=8'b0000_0001; led=8'b0000_0010; led=8'b0000_0100; led=8'b0000_1000; led=8'b0001_0000; led=8'b0010_0000; led=8'b0100_0000; led=8'b1000_0000; led=8'b0100_0000; led=8'b0010_0000; led=8'b0001_0000; led=8'b0000_1000; led=8'b0000_0100; led=8'b0000_0010; led=8'b0000_0001;

always@(posedge w or negedge res) begin if(~res) begin q<=0; en<=1; buzz<=0;

end else begin if(q==2999) begin en<=1; q<=0; buzz<=0; end else begin q<=q+1; en<=0; buzz<=1; end end end endmodule (4)记分译码器的设计 七段译码器是在数学电路设计中经常用到的显示电路。所谓七段译码器,其实 是由 7 段发光二极管组成的用于显示数字的器件。记分译码器(encoder):由于 记分需要显示出来, 所以要使用七段译码器。而状态机中的记分是由 2 个 4 位二 进制码来表示的。以下程序就是实现从 4 位二进制码转换成七段译码显示。 module encoder(in1,in2,out1,out2); input[3:0] in1,in2; output[7:0] out1,out2; reg[7:0] out1,out2; always@(*) case(in1) 8'b0000:out1=8'b1100_0000; 8'b0001:out1=8'b1111_1001; 8'b0010:out1=8'b1010_0100; 8'b0011:out1=8'b1011_0000; 8'b0100:out1=8'b1001_1001; 8'b0101:out1=8'b1001_0010; 8'b0110:out1=8'b1000_0010; 8'b0111:out1=8'b1111_1000; 8'b1000:out1=8'b1000_0000; 8'b1001:out1=8'b1001_0000; default:out1=8'b1100_0000; endcase always@(*) case(in2)

//共阳数码管编码 //0 //1 //2 //3 //4 //5 //6 //7 //8 //9

8'b0000:out2=8'b1100_0000; //0 8'b0001:out2=8'b1111_1001; //1 8'b0010:out2=8'b1010_0100; //2 8'b0011:out2=8'b1011_0000; //3 8'b0100:out2=8'b1001_1001; //4 8'b0101:out2=8'b1001_0010; //5 8'b0110:out2=8'b1000_0010; //6 8'b0111:out2=8'b1111_1000; //7 8'b1000:out2=8'b1000_0000; //8 8'b1001:out2=8'b1001_0000; //9 default:out2=8'b1100_0000; endcase endmodule 这个记分译马电路是针对乒乓球游戏机的特点进行的特别设计, 采用的是全部 列举的方法。

(4)顶层的设计 顶层综合各个模块设计, 这样就完成了数字乒乓球游戏机的 VHDL 源程序编写。 module table_tennis(clk,res,key1,key2,led,smg1,smg2,smg3,smg4,buzz, jia_led,yi_led); input output output[7:0] clk,res,key1,key2; buzz,jia_led,yi_led; led,smg1,smg2,smg3,smg4;

wire[7:0] smg1,smg2,smg3,smg4,led; wire w1,w2,w3,w4,w5,jia_led,yi_led,buzz,win,win1,win2,en,en_jia,en_yi,t1,t 2; and D1(w3,w5,en_jia); and D2(w4,w5,en_yi); or D3(win,win1,win2); state_machine u0(.clk(clk),.res(res),.jia(w1),.yi(w2),.en_jia(w3),.en_yi(w4),.key1( key1), .key2(key2),.led(led),.win(win),.t1(t1),.t2(t2)) ; score1 u1(.res(res),.jia(w1),.s1(smg1),.s2(smg2),.win(win1)); score2

u2(.res(res),.yi(w2),.s1(smg3),.s2(smg4),.win(win2)); Permissions u3(.clk(clk),.res(res),.in1(w1),.in2(w2),.out1(jia_led),.out2(yi_led) ,.en_jia(en_jia),.en_yi(en_yi)); goal u4(.res(res),.jia(w1),.yi(w2),.en(w5),.buzz(buzz)); timer u5(.t1(t1),.t2(t2)); endmodule 从顶层设计中可以看到, 控制整个乒乓球游戏机运转的就是状态机进程,它队 各个外围部分起控制作用。它是整个程序的核心,起到一个中心控制器的作用。 而外围的部分,比如分数显示,球的轨迹,都是通过它传出的信号来控制。状态 机中的信号变化同时就可以影响到外围的显示部分——发光二极管和七段译码 器,从而表示出当时的乒乓球位置和双方分数情况。

第四章 顶层电路、仿真及调试 1.顶层电路图 根据程序所画的顶层电路图如图 3

图3 2.仿真 编译完成后仿真如图

图4 A 方先发球,B 方在恰当的时刻击球成功,当球回到 A 方时,A 方没有及时接到 球的仿真波形

图5 A 方两次成功发球后 B 方都没有接到球,A 方得 2 分的仿真波形图

图6 B 方成功发球后,A 方在恰当的时刻成功接到球,而 B 方没有接到球的情况 3、实验调试结果 通过调试我们可以观察到,8 个 LED 排成一条直线,以中点为界,两边各代表 参赛双方的位置, 其中一只点亮的 LED 指示球的当前位置,点亮的 LED 依此从左 到右,或从右到左,其移动的速度应能调节。当“球”(点亮的那只 LED)运动到 某方的最后一位时, 参赛者应能果断地按下位于自己一方的按钮开关,即表示启 动球拍击球。若击中,则球向相反方向移动;若未击中,则对方得 1 分。一方得 分时,电路自动响铃 3 秒,这期间发球无效,等铃声停止后方能继续比赛。设置 自动记分电路,甲、乙双方各用 2 位数码管进行记分显示,每计满 21 分为 1 局。 甲、乙双方各设一个发光二极管,表示拥有发球权,每隔 5 次自动交换发球权, 拥有发球权的一方发球有效。 第五章

结论
采用 Verilog HDL 语言编程,基于 FPGA 成功设计了一款乒乓球比赛的游戏 机,通过仿真验证可知,结果满足了设计的要求。系统能够模拟两人乒乓球比赛 的基本过程和规则,并能自动裁判和计分,能有效模拟实际的乒乓球比赛。该系 统可进一步改进,改进思路为:改用人体感应器官来采集击球信号,采用 FPGA 产生视频信号送到电视机, 更直观地展示乒乓球运动轨迹,从而真正实现人机互 动,优化虚拟效果。

参考文献:
[1]朱正伟.EDA 技术及应用[M].清华大学出版社,2005 年 10 月第一版. [2]刘昌华.EDA 技术与应用—基于 QuartusⅡ和 VHDL[M].北京航空航天大学出 版社,2012 年 8 月第一版. [3]潘松 黄继业.EDA 技术实用教程[M].科学出版社,2002 年 10 月第一版. [4]包明.EDA 技术与可编程器件的应用[M].北京航空航天大学出版社,2007 年 10 月第一版. [5]江国强.EDA 技术与应用[M].电子工业出版社,2004 年 8 月第一版. [6]张文爱.EDA 技术与 FPGA 应用设计[M].电子工业出版社,2012 年 1 月第一 版. [7]谭慧生 张昌凡.EDA 技术与应用[M].西安电子科技大学出版社,2001 年 9 月第一版. [8]汉泽西.EDA 技术及其应用[M].北京航空航天大学出版社,2004 年 5 月第一 版. [9]王振红.VHDL 数字电路设计与应用教程[M].机械工业出版社,2003. [10]付家才.EDA 技术与 FPGA 应用设计[M].电子工业出版社,2012,(01). [11]赖杰.VHDL 与数字电路设计[M].科学出版社,2001. [12]李慧.PLD 与数字系统设计[M].西安电子科技大学出版社,2005. [13]王行.EDA 技术入门与提高[M].西安:西安电子科技大学出版社,2005. [14]田耘.Xilinx FPGA 开发实用教程[M].清华大学出版社,2008. [15]杨跃.FPGA 应用开发实战技巧精粹[M].人民邮电出版社,2005.

附录: Verilog HDL 语言源程序: module table_tennis(clk,res,key1,key2,led,smg1,smg2,smg3,smg4,buzz, jia_led,yi_led); input output output[7:0] clk,res,key1,key2; buzz,jia_led,yi_led; led,smg1,smg2,smg3,smg4;

wire[7:0] smg1,smg2,smg3,smg4,led; wire w1,w2,w3,w4,w5,jia_led,yi_led,buzz,win,win1,win2,en,en_jia,en_yi,t1,t 2; and D1(w3,w5,en_jia); and D2(w4,w5,en_yi); or D3(win,win1,win2); state_machine u0(.clk(clk),.res(res),.jia(w1),.yi(w2),.en_jia(w3),.en_yi(w4),.key1( key1), .key2(key2),.led(led),.win(win),.t1(t1),.t2(t2) ); score1 u1(.res(res),.jia(w1),.s1(smg1),.s2(smg2),.win(win1)); score2 u2(.res(res),.yi(w2),.s1(smg3),.s2(smg4),.win(win2)); Permissions u3(.clk(clk),.res(res),.in1(w1),.in2(w2),.out1(jia_led),.out2(yi_led) ,.en_jia(en_jia),.en_yi(en_yi)); goal u4(.res(res),.jia(w1),.yi(w2),.en(w5),.buzz(buzz)); timer u5(.t1(t1),.t2(t2)); endmodule

module state_machine(clk,res,key1,key2,led,jia,yi,en_jia,en_yi,win,t1,t2); // input clk,res,key1,key2,en_jia,en_yi,win,t1; output jia,yi,t2; reg jia,yi,t2; output[7:0] led;

reg[7:0] led; parameter[7:0] s0=8'b0000_0001,s1=8'b0000_0010,s2=8'b0000_0100,s3=8'b0000_1000, s4=8'b0001_0000,s5=8'b0010_0000,s6=8'b0100_0000,s7=8'b1000_0000; reg[7:0] current_state,next_current; /*--------------------每个时钟沿转跳一次逻辑状态 --------------------*/ always@(posedge clk or negedge res) begin if(~res) current_state<=s0; else current_state<=next_current; end /*--------------------产生下一状态的组合逻辑 -----------------------*/ always@(current_state) begin case(current_state) s0: if((key1==1)&&(en_jia==1)) next_current<=s1; else if((key2==1)&&(en_yi==1)) next_current<=s2; else next_current<=s0; s1: if(led==8'b0000_0001) next_current<=s3; else next_current<=s1; s2: if(led==8'b1000_0000) next_current<=s4; else next_current<=s2; s3: if((t1==1)&&(key2==1)) next_current<=s2; else next_current<=s5; s4: if((t1==1)&&(key1==1)) next_current<=s1; else next_current<=s6; s5: if(win==1) next_current<=s7; else if((en_jia==1)||(en_yi==1)) next_current<=s0; else next_current<=s5; s6: if(win==1) next_current<=s7;

else if((en_jia==1)||(en_yi==1)) else

next_current<=s0; next_current<=s6;

endcase end /*--------------------状态机输出的组合逻辑 -------------------------*/ always@(posedge clk or negedge res) begin if(~res) begin led<=8'b0000_0000; t2<=0; jia<=0; yi<=0; end else begin case(current_state) s0: led<=8'b0000_0000; s1: begin led=8'b1000_0000; #1000 led=8'b0100_0000; #1000 led=8'b0010_0000; #1000 led=8'b0001_0000; #1000 led=8'b0000_1000; #1000 led=8'b0000_0100; #1000 led=8'b0000_0010; #1000 led=8'b0000_0001; end s2: begin led=8'b0000_0001; #1000 led=8'b0000_0010; #1000 led=8'b0000_0100; #1000 led=8'b0000_1000; #1000 led=8'b0001_0000; #1000 led=8'b0010_0000; #1000 led=8'b0100_0000; #1000 led=8'b1000_0000; end

s3: begin led<=8'b0000_0001; t2<=1; end s4: begin led<=8'b1000_0000; t2<=1; end s5: jia<=1; s6: yi<=1; s7: begin #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 #1000 end endcase end end endmodule led=8'b0000_0001; led=8'b0000_0010; led=8'b0000_0100; led=8'b0000_1000; led=8'b0001_0000; led=8'b0010_0000; led=8'b0100_0000; led=8'b1000_0000; led=8'b0100_0000; led=8'b0010_0000; led=8'b0001_0000; led=8'b0000_1000; led=8'b0000_0100; led=8'b0000_0010; led=8'b0000_0001;

module score1(res,jia,s1,s2,win); input res,jia; output win; output[7:0] s1,s2; wire[7:0] s1,s2; wire[3:0] w1,w2; add21 u1(.in(jia),.res(res),.win(win),.out1(w1),.out2(w2)); encoder u2(.in1(w1),.in2(w2),.out1(s1),.out2(s2));

endmodule

module score2(clk,res,yi,s1,s2,win); input clk,res,yi; output win; output[7:0] s1,s2; wire[7:0] s1,s2; wire[3:0] w1,w2; add21 u1(.in(yi),.res(res),.win(win),.out1(w1),.out2(w2)); encoder u2(.in1(w1),.in2(w2),.out1(s1),.out2(s2)); endmodule

module add21(in,res,win,out1,out2); input in,res; output win; output[3:0] out1,out2; reg[3:0] out1,out2; reg cin,win; always@(posedge in or negedge res) begin if(~res) begin out1<=0;cin<=0; end else if(in) if(out1==9)

begin out1<=0; cin<=1; end else begin out1<=out1+1; cin<=0; end end always@(posedge cin or negedge res) begin if(~res) out2<=0; else if(cin) if(out2==9) out2<=0; else out2<=out2+1; end always@(*) begin if((out1==1)&&(out2==2)) win<=1; else win<=0; end

endmodule module encoder(in1,in2,out1,out2); input[3:0] in1,in2; output[7:0] out1,out2; reg[7:0] out1,out2; always@(*) case(in1) //共阳数码管编码 8'b0000:out1=8'b1100_0000; //0 8'b0001:out1=8'b1111_1001; //1 8'b0010:out1=8'b1010_0100; //2 8'b0011:out1=8'b1011_0000; //3 8'b0100:out1=8'b1001_1001; //4

8'b0101:out1=8'b1001_0010; //5 8'b0110:out1=8'b1000_0010; //6 8'b0111:out1=8'b1111_1000; //7 8'b1000:out1=8'b1000_0000; //8 8'b1001:out1=8'b1001_0000; //9 default:out1=8'b1100_0000; endcase always@(*) case(in2) 8'b0000:out2=8'b1100_0000; //0 8'b0001:out2=8'b1111_1001; //1 8'b0010:out2=8'b1010_0100; //2 8'b0011:out2=8'b1011_0000; //3 8'b0100:out2=8'b1001_1001; //4 8'b0101:out2=8'b1001_0010; //5 8'b0110:out2=8'b1000_0010; //6 8'b0111:out2=8'b1111_1000; //7 8'b1000:out2=8'b1000_0000; //8 8'b1001:out2=8'b1001_0000; //9 default:out2=8'b1100_0000; endcase endmodule

module Permissions(clk,res,in1,in2,out1,out2,en_jia,en_yi); input clk,res,in1,in2; output out1,out2,en_jia,en_yi; reg out1,out2,en_jia,en_yi; wire w1,w2,a,b; or add4 select u2(w1,in1,in2); u1(.res(res),.c(w1),.cnt(w2)); u3(.cnt(w2),.a(a),.b(b)); s0=1'b00,s1=1'b01; current_state,next_current;

parameter[1:0] reg[1:0]

always@(posedge clk or negedge res) begin if(!res) current_state<=s0; else current_state<=next_current;

//复位

end always@(current_state or next_current) begin case(current_state) s0: begin out1<=1; en_jia<=1; en_yi<=0; out2<=0; if(a==1) next_current<=s1; else next_current<=s0; end s1: begin out1<=0; en_jia<=0; en_yi<=1; out2<=1; if(b==1) next_current<=s0; else next_current<=s1; end endcase end endmodule

module add4(res,c,cnt); input res,c; output cnt; reg cnt; reg[2:0] q; always@(posedge c or negedge res) begin if(~res) begin q<=0; cnt<=0;

end else begin if(q==4) begin cnt<=1; q<=0; end else begin q<=q+1; cnt<=0; end end end endmodule

module select(cnt,a,b); input cnt; output a,b; reg a,b; always@(cnt) begin if(cnt==1) begin a<=1; b<=0; end else begin a<=0; b<=1; end end endmodule

module goal(res,buzz,jia,yi,en); input res,jia,yi; output buzz,en;

reg reg[12:0] wire or

buzz,en; q; w; u1(w,jia,yi);

always@(posedge w or negedge res) begin if(~res) begin q<=0; en<=1; buzz<=0; end else begin if(q==2999) begin en<=1; q<=0; buzz<=0; end else begin q<=q+1; en<=0; buzz<=1; end end end endmodule

module timer(t1,t2); input t2; output t1; reg t1; reg[8:0] cnt; always@(posedge t2) begin if(cnt==499) begin cnt<=0;

t1<=0; end else cnt<=cnt+1; end endmodule


相关文档

基于FPGA乒乓球比赛游戏机的设计
基于FPGA_乒乓球比赛游戏机_的设计
基于FPGA的乒乓球游戏设计
最新FPGA乒乓球游戏机设计----毕业论文汇编
FPGA乒乓游戏机设计方案
基于FPGA的乒乓球游戏机
基于FPGA乒乓球游戏机Verilog设计
基于FPGA的乒乓球游戏参考设计
基于FPGA的乒乓球游戏机控制器设计
fpga 乒乓球游戏机
电脑版