FPGA编程从零开始 使用Verilog
FPGA编程从零开始
使用Verilog
[美] 西蒙 • 蒙克(Simon Monk) 著
李杨 别志松 译
北 京Simon Monk
Programming FPGAs: Getting Started with Verilog
EISBN:978-1-25-964376-7
Copyright © 2017 by McGraw-Hill Education.
All Rights reserved. No part of this publication may be reproduced or transmitted in any form or
by any means, electronic or mechanical, including without limitation photocopying, recording,
taping, or any database, information or retrieval system, without the prior written permission of
the publisher.
This authorized Chinese translation edition is jointly published by McGraw-Hill Education and
Tsinghua University Press Limited. This edition is authorized for sale in the People’s Republic of
China only, excluding Hong Kong, Macao SAR and Taiwan.
Translation copyright © 2018 by McGraw-Hill Education and Tsinghua University Press Limited.
版权所有。未经出版人事先书面许可,对本出版物的任何部分不得以任何方式或途径复制或传播,
包括但不限于复印、录制、录音,或通过任何数据库、信息或可检索的系统。
本授权中文简体字翻译版由麦格劳-希尔(亚洲)教育出版公司和清华大学出版社有限公司合作出版。
此版本经授权仅限在中国大陆地区销售、不能销往中国香港、澳门特别行政区和中国台湾地区。
版权©2018 由麦格劳-希尔(亚洲)教育出版公司与清华大学出版社有限公司所有。
北京市版权局著作权合同登记号 图字:01-2018-0334
本书封面贴有 McGraw-Hill Education 公司防伪标签,无标签者不得销售。
版权所有,侵权必究。侵权举报电话:010-62782989 13701121933
图书在版编目(CIP)数据
FPGA 编程从零开始 使用 Verilog / ( 美 ) 西蒙 • 蒙克 (Simon Monk) 著;李杨,别志松 译 .—北
京:清华大学出版社,2018
书名原文:Programming FPGAs: Getting Started with Verilog
ISBN 978-7-302-50134-3
Ⅰ . ① F … Ⅱ . ①西… ②李… ③别… Ⅲ . ① 可编程序逻辑器件-系统设计 Ⅳ. ① TP332.1
中国版本图书馆 CIP 数据核字 (2018) 第 112346号
责任编辑:王 军 韩宏志
装帧设计:孔祥峰
责任校对:曹 阳
责任印制:宋 林
出版发行:清华大学出版社
网 址: http://www.tup.com.cn ,http://www.wqbook.com
地 址:北京清华大学学研大厦 A 座 邮 编:100084
社 总 机: 010-62770175 邮 购:010-62786544
投稿与读者服务: 010-62776969 ,c-service@tup.tsinghua.edu.cn
质 量 反 馈: 010-62772015 ,zhiliang@tup.tsinghua.edu.cn
印 装 者:北京嘉实印刷有限公司
经 销:全国新华书店
开 本: 148mm × 210mm 印 张: 6.125 字 数: 153千字
版 次: 2018 年 7 月第 1 版 印 次: 2018 年 7 月第 1次印刷
定 价: 49.80元
——————————————————————————————————————————
产品编号: 078173-01译 者 序
Verilog 是最流行的硬件描述语言之一,已被 IEEE 列为标准硬件
描述语言。其基本语法与 C 语言相近,这一设计初衷使其对于数字电
路研发者更容易理解和上手。
本书作者 Simon Monk 博士是数字电路工程设计领域的专家,撰
写了多本有关开源硬件方面的著作。本书是 Simon Monk 博士的经典
之作,对于使用 Verilog 语言进行 FPGA 开发的初学者来说,是一本绝
佳的入门书籍。虽然对于大部分初学者来说,数字电路设计晦涩难懂,
硬件编程和调试也难以入门,但本书由浅入深、循序渐进,将复杂的
主题讲得简单易懂,将枯燥的技术讲得活灵活现;从基本的数字电路
概念讲起,全面介绍使用 Verilog 进行 FPGA 编程的重要环节,指导你
开始使用 Mojo、Papilio One 和 Elbert 2 这三种流行的 FPGA 开发板。
本书虽然简短,但麻雀虽小,五脏俱全,你将从头到尾为大量工程编
写指令,包括 LED 译码器、计时器、单音生成器,甚至是存储器映射
的视频显示器。作者在该领域经验丰富,提供了多个紧贴实用的开发
实例。相信读者在学习完本书后,能快速设计出自己的第一个作品!
何乐而不为呢?
在这里要感谢清华大学出版社的编辑,他们对本书的翻译和出版
投入了很多心血,保证了本书顺利付梓。本书全部章节由李杨、别志
松翻译,参与翻译的还有王迎、王辞等人。
对于本书,译者在翻译过程中始终保持谨慎的态度,但难免存在
疏漏。如有任何意见和建议,恳请广大读者批评指正。作 者 简 介
Simon Monk 拥有控制和计算机科学学士学位,以及软件工程博
士学位。Simon 是一名全职作家,迄今已撰写多本书籍,包括
Programming Arduino 、 Programming Raspberry Pi 和Hacking
Electronics ,并参与撰写 Practical Electronics for Inventors。Simon 的个
人网站是 MonkMakes.com,Twitter 是@simonmonk2。致 谢
一如既往地感谢 Linda 的耐心和支持。
感谢 TAB/McGraw-Hill 和 MPS Limited 的 Michael McCabe、Patty
Wallenburg 和他们的同事。一如往常,与如此卓越的团队合作非常
愉快。
同时感谢 Duncan Amos 有益而全面的技术审阅。前 言
用自己的芯片完成想做的事,岂不是一件乐事?当然,现场可编
程门阵列(Field-Programmable Gate Array,FPGA)可让你非常接近这一
梦想。FPGA 并非为你专门设计的芯片,而是通用芯片,能被配置用
来完成你希望做的任何事情。
此外,要配置 FPGA,既可绘制原理图,也可使用硬件定义语言
Verilog;如果你的设计是成功的,Verilog 也能用于生产真正的定制芯
片。尽管本书也将展示如何使用原理图编辑器进行设计,但本书的重
点是指导你学习 Verilog 语言。
可根据自己的需要多次修改 FPGA 配置,使其成为原型化设计的
优秀工具。如果设计问题浮出水面,你可对设备重新编程,直到消除
所有漏洞为止。当你意识到可真正配置 FPGA 来包含能运行程序的处
理器时,这种十分出奇的灵活性就会显现出来。
在本书中,你将学习 FPGA 的一般使用原则,将学习本书描述的
示例,并在三种最流行的 FPGA 评估板(Mojo、Papilio One 和 Elbert 2)
上运行这些示例。
尽管从逻辑上讲,微控制器可胜任 FPGA 能完成的大部分工作,
但 FPGA 的运行速度更快;另外,一些人员发现,相对于实现复杂的
算法,描述逻辑门和硬件更简单。你可使用 FPGA 实现微控制器或其
他处理器(以及其他人的工作)。
在其中一种低成本 FPGA 开发板上使用 Verilog 编程,可能最令人
信服的原因仅在于学习一些新知识,收获一些乐趣! VIIIPhoton 物联网编程从零开始
读者可访问https://github.com/simonmonk/pro-fpgas下载本书各章
的项目文件,也可扫描封底的二维码下载。目 录
第 1 章 逻辑 ................................................................................. 1
1.1 逻辑门..................................................................................... 1
1.1.1 非门............................................................................. 2
1.1.2 与门............................................................................. 3
1.1.3 或门............................................................................. 3
1.1.4 与非门和或非门.......................................................... 4
1.1.5 异或门 ......................................................................... 5
1.2 二进制..................................................................................... 6
1.3 添加逻辑................................................................................. 8
1.4 触发器..................................................................................... 9
1.5 移位寄存器........................................................................... 11
1.6 二进制计数器....................................................................... 12
1.7 小结....................................................................................... 13
第 2 章 FPGA............................................................................. 15
2.1 FPGA 的工作原理................................................................ 15
2.2 Elbert 2.................................................................................. 17
2.3 Mojo...................................................................................... 18
2.4 Papilio ................................................................................... 20
2.5 软件设置............................................................................... 22
2.5.1 安装 ISE .................................................................... 22
2.5.2 安装 Elbert 软件........................................................ 24 XFPGA 编程从零开始 使用 Verilog
2.5.3 安装 Mojo 软件......................................................... 25
2.5.4 安装 Papilio 软件....................................................... 26
2.6 项目文件............................................................................... 26
2.7 小结....................................................................................... 27
第 3 章 绘制逻辑 ........................................................................ 29
3.1 数据选择器示例 ................................................................... 29
3.1.1 步骤 1:创建一个新项目 ......................................... 30
3.1.2 步骤 2:创建一个新的原理图.................................. 34
3.1.3 步骤 3:添加逻辑符号 ............................................. 36
3.1.4 步骤 4:连接门......................................................... 36
3.1.5 步骤 5:添加 IO 标记............................................... 37
3.1.6 步骤 6:创建用户约束文件...................................... 38
3.1.7 步骤 7:生成.bit 文件............................................... 42
3.1.8 步骤 8:编写开发板................................................. 44
3.1.9 测试结果 ................................................................... 46
3.2 一个 4 位计数器示例............................................................ 48
3.2.1 绘制原理图................................................................ 49
3.2.2 实现约束文件............................................................ 49
3.2.3 测试计数器................................................................ 52
3.3 小结....................................................................................... 52
第 4 章 Verilog 简介.................................................................... 53
4.1 模块....................................................................................... 53
4.2 引线、寄存器和总线............................................................ 54
4.3 并行执行............................................................................... 54
4.4 数字格式............................................................................... 54
4.5 使用 Verilog 编写的数据选择器 .......................................... 55
4.6 使用 Verilog 编写的计数器.................................................. 59
4.7 同步逻辑............................................................................... 62 目 录XI
4.8 小结....................................................................................... 62
第 5 章 模块化 Verilog ................................................................ 63
5.1 七段译码器........................................................................... 63
5.2 按钮去抖............................................................................... 68
5.3 复用七段显示器和计数器.................................................... 73
5.3.1 项目结构 ................................................................... 74
5.3.2 display_7_seg............................................................. 76
5.3.3 counter_7_seg ............................................................ 79
5.3.4 用户约束文件............................................................ 81
5.3.5 导入模块源代码........................................................ 82
5.3.6 设置顶层模块............................................................ 82
5.3.7 3 数位版本................................................................. 83
5.3.8 测试........................................................................... 83
5.4 小结....................................................................................... 84
第 6 章 计时器示例..................................................................... 85
6.1 状态机................................................................................... 85
6.2 状态机设计........................................................................... 87
6.3 硬件....................................................................................... 88
6.3.1 你之所需 ................................................................... 88
6.3.2 构建........................................................................... 88
6.4 模块....................................................................................... 90
6.5 用户约束文件....................................................................... 91
6.6 计时器模块........................................................................... 92
6.6.1 输入和输出................................................................ 92
6.6.2 按压按钮 ................................................................... 92
6.6.3 报警器实例................................................................ 93
6.6.4 建模时间和显示........................................................ 93
6.6.5 状态机实现................................................................ 94 XIIFPGA 编程从零开始 使用 Verilog
6.6.6 任务........................................................................... 96
6.7 测试....................................................................................... 98
6.8 小结....................................................................................... 98
第 7 章 PWM 和伺服电机........................................................... 99
7.1 脉冲宽度调制....................................................................... 99
7.2 PWM 模块 .......................................................................... 100
7.2.1 PWM 模块输入和输出............................................ 101
7.2.2 PWM 测试模块....................................................... 101
7.2.3 试一试 ..................................................................... 104
7.3 伺服电机............................................................................. 104
7.4 硬件..................................................................................... 105
7.4.1 你之所需 ................................................................. 105
7.4.2 构建......................................................................... 106
7.5 伺服模块............................................................................. 109
7.6 小结..................................................................................... 112
第 8 章 音频 ..............................................................................113
8.1 单音生成............................................................................. 113
8.2 Mojo 的音频输出................................................................ 115
8.3 通用音/频发生器 ................................................................ 116
8.3.1 单音模块 ................................................................. 116
8.3.2 tone_tester 模块 ....................................................... 118
8.3.3 测试......................................................................... 118
8.4 播放音频文件..................................................................... 121
8.4.1 音频文件 ................................................................. 121
8.4.2 RAM........................................................................ 122
8.4.3 wav_player 模块 ...................................................... 122
8.4.4 测试......................................................................... 125
8.4.5 准备自己的音频...................................................... 125 目 录XIII
8.5 小结..................................................................................... 128
第 9 章 视频 ............................................................................. 129
9.1 VGA.................................................................................... 129
9.2 VGA 定时同步 ................................................................... 132
9.3 绘制矩形............................................................................. 133
9.3.1 VGA 模块................................................................ 134
9.3.2 VGA 和 Elbert 2 ...................................................... 137
9.4 使物体运动......................................................................... 138
9.5 存储器映射显示 ................................................................. 141
9.6 小结..................................................................................... 143
第 10 章 扩展内容 .................................................................... 145
10.1 仿真................................................................................... 145
10.2 更深层次的内容 ............................................................... 146
10.3 核和软处理器................................................................... 147
10.4 更多 Papilio 内容.............................................................. 147
10.5 更多 Mojo 内容................................................................. 149
10.6 小结................................................................................... 150
附录 A 资源.............................................................................. 151
附录 B Elbert 2 参考................................................................. 155
附录 C Mojo 参考..................................................................... 165
附录 D Papilio One 参考........................................................... 173 第 1章
逻 辑
现场可编程门阵列(FPGA)是依赖数字逻辑的数字设备。计算机硬
件使用数字逻辑。每一次计算、渲染到显示屏上的每一个像素,以及
音轨中的每一个音符都可使用数字逻辑的构件来创建。
虽然相对于物理电子器件,数字逻辑有时更像一个抽象的数学概
念,但数字逻辑的逻辑门和其他部件是由晶体管蚀刻到集成电路
(Integrated Circuit,IC)上进行构建的。在 FPGA 中,通过绘制逻辑门
来设计电路,然后将逻辑门映射到 FPGA 上的通用门并连接在一起,
以实现逻辑设计。另外,也可使用 Verilog 或其他硬件描述语言来描述
逻辑。
你可购买包含少量逻辑门的芯片,例如具有四个两输入 NAND 门
的 7400 芯片。然而,这些芯片实际上仅用于维护使用它们的旧系统,
或用于教学用途。
1.1 逻辑门
逻辑门具有输入和输出。这些数字输入和输出可以是高电平或低
电平。低数字输入或输出由接近 0V(接地)的电压表征。高数字输入通
常超过逻辑电源电压的一半,高数字输出是正电源电压。FPGA 电源
电压通常为 1.8V、3.3V 或 5V,大部分 FPGA 可运行于一段电压范围 2FPGA 编程从零开始 使用 Verilog
内,有些允许在一台设备上使用多个逻辑电压。
逻辑门的描述较为复杂,因为逻辑门的名称(非、与、或等)在英
文中也有对应的含义。为避免混淆,本书将门名称大写。
1.1.1 非门
最简单的逻辑门是非(NOT)门,有时称为反相器。它具有单输入
和单输出。如果输入是高电平,则输出是低电平;反之亦然。图 1-1
显示了非门的原理图符号。真值表列出每种可能的输入组合及对应输
出。按照惯例,输入命名时,使用字母表开头的字母,如 A、B 和 C。
输出命名时,通常是 Q 或临近字母表结尾处的字母,如 X、Y 和 Z。
图 1-1 非门
为描述逻辑门或一组逻辑门的行为,可使用真值表。真值表定义
逻辑为每个可能的输入或输出组合提供的输出。非门的真值表如表 1-1
所示。字母 H 和 L 或数字 1 和 0 用于代替高(high)和低(low)。
表 1-1 非门的真值表
输入 输出 L H H L如果将一个非门的输出连接至第二个非门,如图 1-2 所示,组合
电路的输出一定为输入本身。

图 1-2 两个非门
1.1.2 与门
顾名思义,与门的输出仅在其所有输入都为高电平时为高。图 1-3
显示了两输入与门的符号,表 1-2 是与门的真值表。
图 1-3 与门
表 1-2 与门的真值表
输入 A 输入 B 输出 Q L L L L H L H L L H H H1.1.3 或门
顾名思义,任一输入为高电平时,或门的输出为高。图 1-4 显示
了两输入或门的符号,表 1-3 是或门的真值表。
图 1-4 或门
表 1-3 或门的真值表
输入 A 输入 B 输出 Q L L L L H H H L H H H H4
FPGA 编程从零开始 使用 Verilog
1.1.4 与非门和或非门
图 1-1 所示的非门输出的小电路表明了门的反相功能。与非门
(NOT AND,NAND)是与门的输出反相,而或非门(NOT OR,NOR)
是或门的输出反相。图 1-5 显示了这两个门的符号,表 1-4 和表 1-5
分别是这两个门的真值表。
图 1-5 与非门和或非门
表 1-4 与非门的真值表
输入 A 输入 B 输出 Q L L H L H H H L H H H L表 1-5 或非门的真值表
输入 A 输入 B 输出 Q L L H L H L H L L H H L与非门和或非门都作为通用门(universal gate)来描述,因为通过对
输入和输出的反相,二者均可作为其他类型的门使用。另外,可组合
一个与非门和一个或门的输入形成一个非门。例如,图 1-6 显示了如 何使用三个或非门组成一个与门。
图 1-6 使用三个或非门组成一个与门
德摩根定律
图 1-6的设计使用了一条逻辑法则,称为德摩根定律,其最佳解
释如下:将两个输入“做与”后进行反相的结果等于将两个输入分别
反相再“做或”。在图 1-6 中,实际有两个输入通过非门进行反相 (输
入绑定在一起的或非门 ),然后“做或”,门对其自身反相,整体的结
果等同于将所有输入“做与”。这是一个有用的技巧。
可通过表 1-6 所示的真值表检查德摩根定律,该表分别包含 A 和
B 的中间状态,以及 A 和 B 的反相结果。
表 1-6 由或非门实现与门的真值表
输入 A 输入 B 非 A 非 B 输出 Q L L H H L L H H L L H L L H L H H L L H1.1.5 异或门
之前讨论的或门是一种包含性的或运算,即 A 或 B 之一为高,或
者二者均为高时,其结果为高。在英语中,“或”的意思是排除性的或。
你想要奶油或冰淇淋作为甜点——暗指不允许同时享用二者。逻辑门
中这种类型的或称为异或(exclusive OR,XOR)。异或非常有用,因为
它允许对输入进行比较。不管输入为高或低,只要输入不同,则异或的输出为高。
图 1-7 显示了如何使用四个与非门构建一个异或门。因为异或门
经常使用,在与非门符号右侧显示异或门的符号。表 1-7 显示了异或
门的真值表。
图 1-7 由四个与非门构建一个异或门
表 1-7 异或门的真值表
输入 A 输入 B 输出 Q L L L L H H H L H H H L1.2 二进制
如果不把逻辑门的输入和输出看成高电平或低电平,而将其认为
是数字(1 表示高电平,0 表示低电平),那么可开始考虑计算机如何使
用逻辑门进行数字运算。然而,仅有数字 0 和 1 并不能满足要求。
我们使用十进制数,即使用 10 个符号表示数字 、1、2、3、4、
5、6、7、8 和 9。这样做可能是因为我们有 10 根手指。当我们需要表
示大于 9 的数字时,不得不使用两个符号——10、45、99,等等。最
右边的数(称为最低有效数字)是个位,向左一位的数字是十位,再向
左一位是百位,依此类推。
如果我们决定利用鼻子数而不是手指数进行计数,则会使用二进
制数。你有 0 或 1。如果你想表示一个大于 1 的数字,则必须采用多个数位。一个二进制数字称为“位”。通常,为了更有意义,我们需要
将几个位组合在一起,就像需要一组十进制数组合在一起以表示更大
的数字一样。
表 1-8 显示了如何使用 3 位表示数字 0 至 7。注意在显示二进制
数字时,通常包含前面的零。
表 1-8 数字 0 至 7 的二进制和十进制表示
十进制数 二进制数 0 000 1 001 2 010 3 011 4 100 5 101 6 110 7 111如果分解一个十进制数,例如 123,可将其写作 1×100+2×10+3。
如果想知道一个二进制数的十进制表示形式,也可使用该方法。例如,
二进制数 111 是十进制数 1×4+1×2+1=7。如果要将更大的二进制数转
换 为 十 进 制 数 , 例 如 100110 , 则 十 进 制 取 值 为
1×32+0×16+0×8+1×4+1×2 +0=38。
一个字节是 8 位,则其数位的十进制分别等价于 128、64、32、
16、8、4、2 和 1。如果将其相加在一起,意味着可表示 0 至 255 之间
的任意十进制数。每次增加一位,可将表示的数字范围加倍。数字很
快就变得很大。现代计算机每次进行 64 位运算,数字范围从 0 一直到
将近 18 000 000 000 000 000 000。1.3 添加逻辑
逻辑门允许基于二进制数进行运算。因为二进制数是由位表示的
数,使用逻辑门可执行任意算术运算。图 1-8 显示了使用逻辑门构建
二进制加法器。
进位输出
图 1-8 一位加法器
表 1-9 显示了一位加法器的真值表。然而,此次有两个输出:加
和和进位。在加法运算结果太大而超出 1 位的表示范围时,进位为 1。
表 1-9 一位加法器的真值表
输入 A 输入 B 输出加和(A+B) 输出进位 0 0 0 0 1 1 1 0 1 1 1 0 1观察此表会发现,如果 A 和 B 均为 ,则和为 。如果二者之一
为 1,则和为 1。然而,如果 A 和 B 均为 1,则和数位自身为 ,但我
们希望下一个数位进位为 1。在二进制中,1+1 为 0 进 1(或二进制 2)。
如此操作意味着,如果我们一次进行多位加和,则下一个加运算
步骤有三个输入(A、B 和进位)。这有点复杂,因为我们不得不将三个
数相加,而不是两个。图 1-9 显示了一个运算进位数的加法器步骤。进位输入
进位输出
图 1-9 带有一个进位的一位加法器
你从不需要用多个门来创建这样的加法器,因为在设计中加法器
可作为单个组件直接使用。然而,理解如何从基本的门组建加法器是
很有趣的。
如果我们有八个这样的步骤,可用它们执行 2 字节加法。每个计
算机的中央处理器(Central Processing Unit,CPU)都有硬件加法器,硬
件加法器由以类似方式实现的逻辑门组成。32 位处理器一次能处理 32
位数,64 位处理器具有 64 阶如图 1-9 所示的加法步骤,一次加 64 位。
1.4 触发器
在前述示例中,逻辑门被安排为:对于给定的输入取值集合,总
将获得相同的输出。如果将逻辑门的输出反馈给影响首个门输入的前
列逻辑门,将为逻辑门网络赋予记忆功能。这类逻辑门称为时序逻辑
(sequential logic)。
电子器件的一个有趣之处在于,它是一个较新的领域,其先驱抛弃早期浮夸的拉丁命名规则,将从一个状态跳跃至另一个状态的元件
称为触发器(flip-flop)。对于怀念其较无趣名称的人们来说,也可称其
为双稳态(bistable)。
置位-复位触发器图 1-10 所示的原理图称为置位-复位(Set-Reset,SR)触发器。在引
入将高电平视为 1、低电平视为 0 的思想后,我们继续用数字表示逻
辑值。设想开始时,S 置位为 1,R 复位为 。因为或非门 A 的一个输
入(S)为 1,无论或非门 A 的另一输入是否为 1,输出都为 。或非门
A 输出为 0 意味着,或非门 B 的顶层输入也将为 。或非门 B(R)的另
一输入也为 ,因此或非门 B 的输出为 1,使得或非门 A 下面的输入
也为 1。接下来,S 置位为 ,这对或非门 A 的输出没有影响。
通过将 S 置位为 1,输出 Q 置位为 1,无论我们如何置位 S,Q
都不会改变:仅在 R 置位为 1 时才会改变,此时使得或非门 B(Q)的输
出为 。或非门 A 的输出标记为 Q 加上画线。该线(称之为条)表明对
输出反相, Q表示对 Q 取反。
图 1-10 置位-复位触发器
上述 SR 触发器只是偶尔用到,而最常用、最灵活的触发器类型
是 D 触发器。可通过一个与非门或者或非门构建 D 触发器,你仅需要
将其用作逻辑块。图 1-11 显示了 D 触发器的符号。
图 1-11 D 触发器
D 触发器仍有管脚 S、R、Q 和Q,但它有两个额外的管脚 D 和
CK(时钟)。时钟符号通常显示为一个小的三角凹槽。你仍可使用管脚
S 和 R 分别置位和复位触发器,但更可能使用管脚 D 和 CK。
有时,时钟概念对于数字电路是至关重要的。它同步系统,这样,
逻辑门由高位变化到低位(以及通过多径传播)所引起的微小时延在输
出尚未结束时,不会引起差错。时钟通常连接至一个在高位和低位来
回跳变的信号。本书在大部分示例中,FPGA 具有一个内置的时钟信
号。该信号为 12~50MHz,具体取决于本书示例所用的 FPGA 开发板,
每秒有 12 万~5000 万个高/低位时钟周期。
当时钟信号变为高位时,无论 D 的取值如何(0 或 1),都会被锁定
至输出 Q。这听起来令人失望,但它可用于构建更常用的移位寄存器
和计数器。下面将介绍移位寄存器和计数器。
1.5 移位寄存器
图 1-12 显示了四个 D 触发器,前一个 D 触发器的输出 Q 作为与
其连接的后一个 D触发器的输入。D触发器的所有时钟都连接在一起。
这样的布置称为串行-并行移位寄存器。
图 1-12 使用 D 触发器的 4 位串行-并行移位寄存器
假设时钟和 D 都连接到按压开关,QA 和 QD 均连接到发光二极
管(Light-Emitting Diode,LED),LED 在高位时点亮。按压 D 开关(使
其处于高位),在短暂按压时钟开关时,D 的高位将锁定到第一个触发
器。释放 D 开关(使其处于低位),在重新短暂按压时钟时,将同时发
生两件事情:
(1) QA(高位)的值将被锁定到第二个触发器,使 QB 为高。
(2) D(低位)的当前值将被锁定到第一个触发器,使 QA 为低。
每次脉冲调节时钟为高时,位模式将向右连续输入触发器。注意
时钟从 0 变为 1 时,触发位模式向右输入。可随意添加更多 D 触发器。
1.6 二进制计数器
D 触发器是非常灵活的组件。将上一个触发器的反相输出 Q连接
至下一个触发器作为时钟输入(如图 1-13 所示),触发器将进行二进制
计数。
每次时钟信号改变时,触发器 A 在 0(低)和 1(高)之间切换状态。
触发器的输出以半频为下一阶段提供脉冲,依此类推。这也是将计数
器称为分频器的原因。因此,如果时钟频率为 24MHz,QA 的频率将
为 12MHz,沿电路布线类推,每次减半。
图 1-13 将 D 触发器用作计数器分频器
数字手表和时钟通常运行在 32.768kHz 的时钟频率上。之所以选
择这一频率,是因为如果进行两个 15 次分频,则频率为 1Hz(每秒 1
个脉冲)。在第 3 章和第 4 章中,你将在自己的 FPGA 开发板上实现计
数器示例,首先将其绘制为类似图 1-13 的原理图,然后编写数行
Verilog 代码。
1.7 小结
通过本章的学习,你对广泛而复杂的数字电路领域有了初步认识。
在后续各章,你将接触各种类型的数字器件。
在使用 FPGA 时,理解数字电路基础知识是十分重要的;不过,
即使你不精确理解在软件门级别创建的电路,也可使用诸如 Verilog
的语言创建复杂的设计。在第 2 章,你将了解 FPGA 到底是什么,并
接触本书使用的 FPGA 开发板:Elbert V2、Mojo 和 Papilio。 第 5章
模块化 Verilog
设计一个复杂的 FPGA 系统时,将所有 Verilog 代码放入一个模块
未尝不可。但分治更便于他人理解你所实现的实例。这是因为他们可
推断子模块的角色,并在实际执行每个模块前看到一个更宏大的场景,
即所有这些模块组合在一起的功能。将实例分为小模块,更便于将你
在一个项目中使用的模块应用于其他项目,或将其共享给他人以便在
他们的项目中使用。
创建包含多个模块的项目时,通常有一个顶层模块。顶层模块将
所有子模块以及与 UCF 关联的模块放在一起。与 UCF 关联的模块用
于映射设计中的 FPGA 信号 IO 管脚。
本章首先使用七段译码器模块和第 4 章介绍的计数器模块,并扩
展该示例,直到最终实现一个简单的十进制计数器,能在按下 UP 和
DOWN 开关时,在七段多数位显示器上加减计数。
既可使用此处的指令组建项目,也可从 GitHub 下载完整项目,如
第 2 章末尾处所述。
5.1 七段译码器
我们要组建的第一个可重用模块是一个七段译码器。它有 4 位输
入。输入的数字(即数字 0 至 9)会被译码至正确的段位模式,并在七段显示器上显示。
图 5-1 显示了七段显示器的组织方式。每一段都给定 A~G 之间的
一个字母,并连接至 DP (Decimal Point)。
创建一个新项目,命名为 decoder_7_seg。也可从本书的下载资料
中找到该项目,名为 ch05_decoder_7_seg。
图 5-1 七段显示器
在 src目录中创建一个新的 Verilog源文件,命名为 decoder_7_seg。
在该文件中,编写或复制如下代码:
module decoder_7_seg(
input CLK,
input [3:0] D,
output reg [7:0] SEG
);
always @(posedge CLK)
begin
case(D)
4d0: SEG <= 8b00000011;
4d1: SEG <= 8b10011111; 第 5 章 模块化 Verilog65
4d2: SEG <= 8b00100101;
4d3: SEG <= 8b00001101;
4d4: SEG <= 8b10011001;
4d5: SEG <= 8b01001001;
4d6: SEG <= 8b01000001;
4d7: SEG <= 8b00011111;
4d8: SEG <= 8b00000001;
4d9: SEG <= 8b00001001;
default: SEG <= 8b11111111;
endcase
end
endmodule
模块开头有一个 CLK(时钟)输入,因为我们想将模块与 FPGA 时
钟同步。这里也有一个 4 位输入 D,包含被显示的数字,以及一个 8
位寄存器 output 作为数位模式显示在 LED 上。
always 块与 CLK 同步,包含一个新构造,称为 case 语句。case
语句是一种简洁方法,可编写具有相同取值条件的完整 if 语句块。此
处的输入 D 是 case 语句的期望取值。如果 D 为 ,则输出段(SEG)设
定为数位模式 00000011。0 表示段位被点亮。因此,参考图 5-1,这
表明段 A、B、C、D、E 和 F 均应被点亮,而段 G(位于中间)和 DP 不
被点亮。D 的每种取值都有单独的数位模式。
case 语句结尾的 default 选项定义了如果 D 的取值未被匹配上时
SEG 被设定的模式。因为 D 有 4 位取值,这意味着超过十进制数 9
的任意取值都会导致所有数段关闭。
该模块被设计为一个有用的部件,可在其他项目中使用。作为尝
试,你可创建另一个使用该模块的模块,新模块也可实现其他功能,
例如禁用除 1 之外的所有数位。因此创建一个新的源文件,命名为
seg_test,在其中编写如下代码:
module seg_test(
input CLK,
input [3:0] D, 66FPGA 编程从零开始 使用 Verilog
output [7:0] SEG,
output [3:0]DIGIT
);
assign DIGIT = 4b1110;
decoder_7_seg decoder(.CLK (CLK), .SEG (SEG), .D (D));
endmodule
该模块的输入和输出是:
● CLK:时钟
● D:Data,4 位,每一位都配置一个滑动开关
● SEG:8 位,每一位对应一个数段
● DIGITS:4 位,控制待使能的数字
注意如何初始化 DIGITS,以便使用 0 的反相逻辑使能第一个数
位。然后是一行非常有趣的代码:
decoder_7_seg decoder(.CLK (CLK), .SEG (SEG), .D (D));
这行代码创建一个名为 decoder 的 decoder_7_seg,“(”和“)”之
间的参数声明了 decoder 连接至 seg_test 引线和寄存器的方式。“.”符
号后的名称是译码器中的参数名,而括号“()”中的名称是参数应链
接的 seg_test 中的 wires(引线)或 regs(寄存器)。
最后,需要为项目创建一个 UCF 文件。下面列出 Mojo 下的 UCF
文件,你可在本书的下载资料中找到其他开发板下相同的 UCF 文件。
# User Constraint File for 7-seg decoder implementation
# on Mojo with IO Shield
NET "CLK" LOC = P56;
# 7-segments
NET "SEG[7]" LOC = P5;
NET "SEG[6]" LOC = P8;
NET "SEG[5]" LOC = P144;
NET "SEG[4]" LOC = P143;NET "SEG[3]" LOC = P2;
NET "SEG[2]" LOC = P6;
NET "SEG[1]" LOC = P1;
NET "SEG[0]" LOC = P141;
# Digits
NET "DIGIT[3]" LOC = P12;
NET "DIGIT[2]" LOC = P7;
NET "DIGIT[1]" LOC = P10;
NET "DIGIT[0]" LOC = P9;
# Inputs to slide switches 0 to 3
NET "D[0]" LOC = P120 | PULLDOWN;
NET "D[1]" LOC = P121 | PULLDOWN;
NET "D[2]" LOC = P118 | PULLDOWN;
NET "D[3]" LOC = P119 | PULLDOWN;
为测试该项目,将其上传到开发板,然后尝试移动滑动开关得到
不同的取值。图 5-2 显示了该测试功能。
图 5-2 测试七段译码器模块
5.2 按钮去抖
尝试计数器项目时,你可能注意到某个开关抖动会导致计数器跳
过一些数字。我们可创建一个去抖模块,该模块可用于使用开关的任
意项目中。
推下开关时,实际需要解决三个问题。首先,按下按钮就其特性
而言并不会与时钟同步。其次,我们实际上因为按钮弹跳的机械解除
而移除了不必要的状态转移。最后,你实际上对按钮由 OFF 至 ON(反
之亦然)的转移触发事件更感兴趣,而不是对其处于 ON 或 OFF 状态
时某个特定时间的事件感兴趣。
可综合分析这三个特点,并使用一个非常有用的模块解决它们,
该模块将一个潜在的抖动开关作为输入,提供了一个干净的、同步的
按钮的二进制状态,以及一对有用的附加输出,在按钮状态转移时单
个时钟周期电平上升。你可在 debounce 项目中找到开关去抖项目。因
为这里对代码进行了讲解,你可能希望在 ISE 环境中将其公开。
可从 www.fpga4fun.com/Debouncer.html 和 umich.edu/
courses/eecs270/270lab/270_docs/debounce.html 下载该代码。
首先将按钮开关与使用一对寄存器的时钟进行同步。此处列出
debouncer.v 文件和代码注释。可在下载文件 ch05_debouncer 中找到演
示该模块用法的项目。
module debouncer(
input CLK,
input switch_input,
output reg state,
output trans_up,
output trans_dn
);
// Synchronize the switch input to the clock
reg sync_0, sync_1;
always @(posedge CLK)
beginsync_0 = switch_input;
end
always @(posedge CLK)
begin
sync_1 = sync_0;
end
此后,在以下去抖代码中使用第二个寄存器(sync_1)的输出:
// Debounce the switch
reg [16:0] count;
wire idle = (state == sync_1);
wire finished = &count; // true when all bits of count
are 1s
去抖自身通过使用一个计时器(本例中是一个 16 位计时器)来工
作。它忽略任何开关状态的转移,直至计时器到达最大值。在 16 位计
时器的情况下,有 65 536 个时钟周期,Mojo 的 50MHz 时钟下每个周
期为 65 536/50 000 000=1.3ms。在 Elbert 2 的 12MHz 时钟下,每个周
期为 5.4ms;在 Papilio 的 32MHz 时钟下,每个周期为 2ms。通常开关
抖动会在一毫秒内被很好地解决,对用户来说,5.4ms 依然是一个非
常短的时间,因此在本书中使用其他开发板的情况下,该段代码也不
会被修改,尽管开发板的时钟周期并不一样。
确定计数器寄存器大小
大部分 FPGA项目都需要一个或多个计数器;在传统编程语言中,
整数变量具有固定大小(例如 32 位或 64 位),但在 FPGA 中,每个向
量可能有不同的大小。为确定使用多少位,你需要掌握一些数学知识。
1 位计数器仅能保持最大取值范围为 0 至 1,而 2 位计数器的取值范围
为 0 至 3,3 位计数器为 0 至 7。计数器的取值上限为 2 的 N 次幂减 1,
N 为计数器的位数。表 5-1 可帮助你选择特定取值所需的位数。表 5-1 确定计数器大小
位数 最大取值(十进制) 1 1 2 3 3 7 4 15 5 31 6 63 7 127 8 255 9 511 10 1023 11 2047 12 4095 13 8191 14 16 383 15 32 767 16 65 535 17 131 071 18 262 143 19 524 287 20 1 048 575idle 引线的取值被设定为与 sync_1 对比状态的结果。如果相同,
则开关不会发生状态转移,idle 取值为 1;否则,取值为 。与诸如 Java
和 C 的编程语言一样,==运算符用于比较二者是否相同。
逻辑运算符&用于判断计数器是否达到最大值。当使用多位寄存
器(如 count)时,结果是所有比特一起执行 AND操作。该状态与 finished
引线关联。always @(posedge CLK)
begin
if (idle)
begin
count <= 0;
end
else
begin
count <= count + 1;
if (finished)
begin
state <= ~state;
end
end
end
主 always 块同步至时钟的正边沿,如果未发生事件,复位计数为
。如果发生状态转移,计数器加 1。当计数器结束时,切换 state 输
出。在 Verilog 中,~符号表示取反。
接下来定义有用的附加输出 trans_dn 和 trans_up,它们分别在按
下和释放按钮时,使一个时钟周期电平变高。这是对 idle 信号输出取
反、计时器是否已经“结束”以及 state 输出一起进行 AND 操作实现
的。对于 trans_dn,则是对 state 取反,然后一起进行 AND 操作。
assign trans_dn = ~idle & finished & ~state;
assign trans_up = ~idle & finished & state;
endmodule
去抖代码的测试程序解释了该模块可用于所有三个可能的输出。
它使用两个按压按钮,每个切换单独的 LED 状态,一个是 trans_dn(按
下按钮),一个是 trans_up(释放按钮)。第三个 LED 只反映了第一个按
钮的状态。
module debounce(
input CLK,input switch_a,
input switch_b,
output reg led_a,
output reg led_b,
output reg led_c
);
定义了三个引线,s_a_dn(按下开关 a)、s_b_up 和 s_a_state。这些
引线此后链接至三个去抖器 d1 至 d3。查看三个去抖器的首行代码,
可看到去抖器名为 d1,括号中是一些参数。每个参数都成对出现,相
互之间用逗号隔开。第一个参数.clk(CLK)将去抖器的 CLK 信号(领先
CLK 一个时钟周期)链接至去抖器测试模块中使用的 CLK。
第二个参数.switch_input(switch_a)将去抖器的switch_input信号链
接至 switch_a。最后一个参数.trans_dn(s_a_dn)将去抖器的输出 trans_dn
连接至引线 s_a_dn。对其他两个 debouncer 实例重复该过程。然而,
这些实例使用去抖器的输出 trans_up 和 state,并将其分别链接至引线
s_b_up 和 s_a_state。
wire s_a_dn, s_b_up, s_a_state;
debouncer d1(.CLK (CLK), .switch_input (switch_a),
.trans_dn (s_a_dn));
debouncer d2(.CLK (CLK), .switch_input (switch_b),
.trans_up (s_b_up));
debouncer d3(.CLK (CLK), .switch_input (switch_a),
.state (s_a_state));
always 块同步至时钟的正边沿,使用链接至三个去抖器输出的引
线设定三个 LED。
always @(posedge CLK)
begin
if (s_a_dn)
begin
led_a <= ~ led_a;
end
if (s_b_up)
beginled_b <= ~ led_b;
end
led_c <= s_a_state;
end
endmodule
现在你已对开关输入计时,不必考虑该模块是如何工作的,仅在
需要开关去抖的项目中使用即可。为说明这一点,下一节将去抖器和
decoder_7_seg 模块组合成第三个模块,该模块是一个四位计数器的显
示器(在 Elbert 2 示例中为三位计数器)。
5.3 复用七段显示器和计数器
该示例(见图 5-3)显示了多数位的七段显示器。按下 Up 按钮会增
加计数,按下 Down按钮会将其复位为 。可在项目 ch05_counter_7_seg
中找到该示例的文件。
图 5-3 一个复用的七段显示器
所有三个 FPGA 开发板都有复用的七段 LED 显示器。Mojo 开发
板(带有 IO Shield)和 Papilio开发板 (带有 LogicStart MegaWing)有四位
显示器;而 Elbert 2 开发板则有三位显示器。在所有情况下,显示器
引线方式大致相同。图 5-4 显示了 Mojo IO Shield 显示器的原理图;
其他开发板类似,但使用不同的管脚。
每个特定数位的数段都连接至其他数位上相同的数段。换言之,
所有的数段 A 都连接在一起,所有的数段 B 都连接在一起,依此类推。
每个数段然后通过一个晶体管(如图 5-4 中的 R5 所示)连接,连接至一
个通用输入输出(GPIO)管脚。
这意味着,如果你设定 GPIO 管脚 P8 为 HIGH,则所有的数段 B
将被使能。显然,你需要在每个数位上显示不同数字。为此,在移至
下一个数位或设定不同的数段模式前,使用单独的数位控制管脚和对
应数位的数段模式,依次使能每个数位。这发生得如此之快,你无法
察觉——看起来只是每个数位显示不同的数段模式。
5.3.1 项目结构
在详细介绍前,有必要回顾一下该项目中使用的各个模块如何互
相关联(图 5-5)。
模块 counter_7_seg 称为顶层模块(稍后将详细介绍)。开发板上的
UCF 会被关联至此模块。该模块实现了计数器逻辑:取值增加、重置
复位以及使用其他模块运行显示。
counter_7_seg 模块有一个 display_7_seg 实例和两个 debouncer 实
例。display_7_seg 模块负责复用显示器,将其刷新,使其能显示所有
数字。模块 display_7_seg 自身包含我们之前创建的 decoder_7_seg 模
块。它将使用 decoder_7_seg 依次设定每个数位的数段模式。
图 5-4 Mojo IO Shield 显示器原理图
图 5-5 counter_7_seg 项目中使用的模块
5.3.2 display_7_seg
你已经学习了两个模块 decoder_7_seg 和 debouncer(去抖器),因
此我们可以开始学习模块 display_7_seg。此处列出 display_7_seg.v 文
件及其代码解释:
module display_7_seg(
input CLK,
input [3:0] units, tens, hundreds, thousands,
output [7:0] SEG,
output reg [3:0] DIGIT
);除了一个 CLK 信号,该模块还有四个输入(Elbert 2 开发板中是三
个输入)用于每个数位,即个位、十位、百位、千位。两个输入用于
GPIO 管脚,配置为控制数段和数位使能引线。
需要三个向量:
● digit_data 是一个 4 位寄存器,被赋值为一个 4 位数字,然后
被译码为当前待处理的数位。
● digit_posn 是一个 2 位计数器,用于跟踪待显示的数位。
● prescaler 是一个 24 位计数器,用于将 CLK 输入分频,设定刷
新速率。前置分频器(prescaler)并不需要 24 位那么多,但是这
给了我们一个选用更快 FPGA 的机会。
reg [3:0] digit_data;
reg [1:0] digit_posn;
reg [23:0] prescaler;
接下来,display_7_seg 模块需要一个 decoder_7_seg 实例,从而将
一个 4 位数字转换为一个 8 位数段模式。我们只需要其中一个实例,
因为它会被依次用于每个数位。
译码器的输入 D 被连接至 digit_data,SEG 向量被重新传递给译
码器:
decoder_7_seg decoder(.CLK (CLK), .SEG (SEG), .D
(digit_data));
现在是 always 块,其与时钟同步。这对前置分频器计数器加 1。
如果 prescaler 已达 50 000,则需要刷新下一数位。Mojo 开发板 50MHz
的时钟速率意味着每毫秒都会发生该事件。因此四位需要 4ms,刷新
频率为 250Hz,这依然很快,人眼无法识别。如果真的需要,可改变
取值,以匹配其他开发板的时钟频率,但即便是 Elbert 2 的 12MHz 的
时钟,刷新频率也是快得难以察觉。
always @(posedge CLK)
begin
prescaler <= prescaler + 24d1;if (prescaler == 24’d50000) // 1 kHz
begin
如果前置分频器已经达到其下次数位刷新的设定取值和时间,则
需要一系列更新,以便转换到合适的数位数据。
首先将前置分频器重置为 ,对下一次数位刷新开始计时,此后
digit_posn 递增。如果 digit_posn 为 ,则 digit_data 设定为个位。数位
控制管脚(DIGIT)之后被设定为仅使能第一个数位。此时,使用二进制
模式 1110。因为数位控制管脚是“LOW 有效”,所以是 1110 而非 0001。
0 意味着该数位被使能。
对其他数位重复相同的模式:
prescaler <= 0;
digit_posn <= digit_posn + 2d1;
if (digit_posn == 0)
begin
digit_data <= units;
DIGIT <= 4b1110;
end
if (digit_posn == 2d1)
begin
digit_data <= tens;
DIGIT <= 4b1101;
end
if (digit_posn == 2d2)
begin
digit_data <= hundreds;
DIGIT <= 4b1011;
end
if (digit_posn == 2d3)
begin
digit_data <= thousands;
DIGIT <= 4’b0111;
end
end
end
endmodule2 位 digit_posn 计数器会自动由 3 至 0 环回处理,因此不需要单独
复位。Elbert 2 只有三个数位,所以 digit_posn 计数器在第三个数位被
显示后,复位为 。
5.3.3 counter_7_seg
counter_7_seg 模块是顶层模块,将所有模块整合集成。该模块的
参数均连接至 GPIO 管脚,并映射至 UCF 文件中定义的网表。此处列
出 counter_7_seg.v 文件及其代码解释:
module counter_7_seg(
input CLK,
input switch_up,
input switch_clear,
output [7:0] SEG,
output [3:0] DIGIT
);
该项目需要两个按压开关,一个增量计数,一个将显示器清除为
0000。这些开关被链接至引线,并使用去抖模块进行去抖。
wire s_up, s_clear;
debouncer d1(.CLK (CLK), .switch_input
(switch_up), .trans_dn (s_up));
debouncer d2(.CLK (CLK), .switch_input (switch_clear),
.trans_dn (s_clear));
需要四个 4 位寄存器,每个寄存器对应四个数位中的一个。
reg [3:0] units, tens, hundreds, thousands;
display_7_seg实例被引接至CLK和四位寄存器,还引接至SEG和
DIGIT;SEG和DIGIT连接至控制数段和数位选择的FPGA GPIO管脚:
display_7_seg display(.CLK (CLK),
.units (units), .tens (tens),
.hundreds (hundreds), .thousands (thousands),
.SEG (SEG), .DIGIT (DIGIT));大部分事件发生在 always 块中。它检查是否按压了去抖开关
s_up。如果按压,则个位加一。之后是一个更深层次的 if 语句序列,
确定数位是否溢出,如果溢出,则对下一个数位进一,然后将当前数
位复位为 :
always @(posedge CLK)
begin
if (s_up)
begin
units <= units + 1;
if (units == 9)
begin
units <= 0;
tens <= tens + 1;
if (tens == 9)
begin
tens <= 0;
hundreds <= hundreds + 1;
if (hundreds == 9)
begin
hundreds <= 0;
thousands <= thousands + 1;
end
end
end
end
如果按下 Clear 开关,则所有四个数位都被清 :
if (s_clear)
begin
units <= 0;
tens <= 0;
hundreds <= 0;
thousands <= 0;
end
end
endmodule声明位数及基数
如果你目光敏锐,会注意到我在赋值 0 或 1时,数字格式有些不
连贯。有人会说你应该声明位数和基数。实际上,这是个好习惯,但
对于上述这类简短实例,也无关紧要。 ISE会自动调整常数的位数,
以匹配被赋值的向量,而数字 1 和 0在任意基数中都是相同的。
对于非 0 或 1的数字,通过选择位数和基数来避免任何可能的混
淆,是一个好办法。然而,选择十进制基数并非有错——要选择保证
代码最易于理解的基数。
5.3.4 用户约束文件
你也需要一个 UCF 文件。下面列出 Mojo 开发板下的 UCF。也可
在本书的下载文件中找到适用于其他开发板的 UCF 文件。
# User Constraint File for 7-seg counter on Mojo with IO
# Shield
NET "CLK" LOC = P56;
# Switches
NET "switch_up" LOC = "P137" | PULLDOWN;
NET "switch_clear" LOC = "P139" | PULLDOWN;
# 7-segments
NET "SEG[7]" LOC = P5;
NET "SEG[6]" LOC = P8;
NET "SEG[5]" LOC = P144;
NET "SEG[4]" LOC = P143;
NET "SEG[3]" LOC = P2;
NET "SEG[2]" LOC = P6;
NET "SEG[1]" LOC = P1;
NET "SEG[0]" LOC = P141;
# Digits
NET "DIGIT[3]" LOC = P12;NET "DIGIT[2]" LOC = P7;
NET "DIGIT[1]" LOC = P10;
NET "DIGIT[0]" LOC = P9;
5.3.5 导入模块源代码
有几种获取所创建项目中的模块源代码的方法。ISE 偶尔分不清
哪些文件从属于它,我找到将一个模块复制到另一个项目中的问题最
少的步骤,以该示例中的去抖器或 decoder_7_seg 为例:
(1) 创建一个新项目。
(2) 打开操作系统文件资源管理器,在项目目录中创建一个 src
目录。
(3) 在该 src 目录中复制添加任意该项目所需的模块(debouncer.v
和 decoder_7_seg.v)。
(4) 在 ISE 中,使用 Add Source…选项,并导航至刚才添加到 src
目录的.v 文件。
5.3.6 设置顶层模块
ISE 中的 Design 选项卡显示项目中所使用的模块之间的关系(如
图 5-6 所示)。
你可在层级结构中看到所有文件都是正确的,counter_7_seg 模块
包含两个去抖器模块,display_7_seg 模块包含一个 decoder_7_seg 模
块。如果仔细看,可发现 counter_7_seg 模块图标紧挨着三个品字排列
的小方块。这说明 counter_7_seg 模块是项目的顶层模块。
ISE通常能够辨识顶层模块,但有时却不能,此时你可通过右击,
并选择选项Set As Top Module,将特定模块指定为顶层模块。
图 5-6 counter_7_seg 项目中所使用的模块
5.3.7 3 数位版本
不同于 Mojo 有四个数位,Elbert 2 只有三个数位,因此大部分情
况下,Elbert 版本的差异仅在于将数位从四减至三。使用三个数位的
一个后果是,在 display_7_seg 中,digit_posn 计数器达到百位时,需
要手动复位为 。
if (digit_posn == 2d2)
begin
digit_data <= hundreds;
DIGIT <= 4b1011;
digit_posn <= 0;
end
5.3.8 测试
组建项目,将其上传至开发板。你会发现每次按下 Up 时,显示器数字会进 1,按下 Clear 按钮时,显示器数字复位为 。耐心地测试
所有四个数位,甚至测试 Elbert 2 开发板下的所有三个数位。
5.4 小结
本章开头介绍 FPGA 的潜在能力。在第 6 章中,我们将重用本章
描述的模块,并继续生成一个倒计时器实例。该计时器实例也引入了
FPGA 系统设计中的一个关键概念,即状态机。购买地址:
https://item.jd.com/12407169.html
扫一扫,关注我们