找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 9161|回复: 7
打印 上一主题 下一主题
收起左侧

大话FPGA书(非常通俗易懂的学习资料)

  [复制链接]
跳转到指定楼层
楼主
玩转 FPGA 前言
一直以来都想写点什么,关于 FPGA 设计,因为有点不太服气。为什么同样是设计,有些人可以好像玩一样的摆弄,photoshop,很时尚,很跩。有些人可以用所谓的广告创意玩弄人们的智商。而硬件工程师则一律的被人以为呆板没有创意。我是说,为什么我们不可以享受设计本身的乐趣,发挥我们自己的创意,为什么就不可以很随心所欲的摆弄我们手里的那些玩具到处炫耀,让美女们看着我们的眼睛都发光?难道一定就是那些冷冰冰的 Datasheet, 死板的 Schedule, 老板的猪肝脸?所以我写这些东西的目的,就是想让大家可以和我一起起来,玩转 FPGA。所以大家千万别抱着学习的态度来看。看累了,不如去打打游戏,看烦了,不如自己去试着做几个项目玩玩。FPGA 是可以玩的,而设计 FPGA 的工具也是很好玩的,为什么?我们先了解一下我们的玩具。和所有的玩具一样,这款玩具也有它的适合玩家。你如果有一些准备,你可以玩得更开心些。首先是语言,你需要至少知道一样设计方式吧。Verilog 或者 VHDL.我个人比较喜欢 Verilog,不过这并不重要。不会?那会画电路图也可以啊。或者呢,会用 Simulink 搭个模型什么的也可以。如果会写一点 C,恐怕就更好了。那么什么是 FPGA (已经知道的朋友尽可以跳过去不看)。想知道学名,去查一下字典,想知道他的历史,去问一下你的教授。我不说这些,因为我们的目的,就是玩。你应该知道芯片吧。对,好像因特尔的 CPU,还有 AMD。好像德州仪器的 DSP。还有 ASIC, ASSP, 反正现在这个世界,没有芯片你恐怕活不下去。而学电子的,如果不知道芯片,那也不用活了。还有一样东西,你一定也知道,就是 CD-RW, DVD-RW.反复可擦写光盘。而 FPGA 就是反复可擦写的芯片。这样你就知道他能有多好玩了,是否还记得第一次拿到 CD-RW 的时候那种惊喜的感觉?想放什么就放什么,想做什么就做什么。你拿到的这东西就是一片巨大的空白,就看玩家怎么往里面塞东西了。 因为他已经是现成的半成品,完全不需要去等待漫长的流片时间,你就可以看到你的设计在板上实现。这就是为什么这个玩具,它可以玩,而且因为是数字电路,所以随便你怎么玩,只要你不插错电源,都可以。不会着火的安全玩具。

下面一个问题是,为什么要玩 FPGA。FPGA 一开始是一种附属的玩具,主要的玩具是 ASIC。人们为了验证 ASIC 设计的功能,往往用 FPGA 来测试。因为他不需要漫长的制造时间。就好像看看这个小孩子长什么样吧,也就知道他长大了大概是什么样子。但是现在这个小孩子居然也慢慢长大了。他开始普遍的被用在非常多的产品里面。首先一个原因是价格。现在的工艺水平是越来越高,这当然是好事情,但是坏事情是,也变得越来越贵。可以想象啊,线宽越来越窄,不是需要更厉害一点的放大镜吗?而这种高工艺导致的高价格是很多产量并不大的 ASIC 无法承受。而 FPGA 作为一个通用的器件呢,它完全可以去追求最新最好的工艺,而让它的买家一起分摊它的成本。它是目前芯片业拥有利润空间最大的一个分支。可以想象,如果有一天,FPGA 公司把这块利润释放出来,对 ASIC,ASSP 设计公司的打击是致命的。第二个原因,就是他可以玩,可以反复擦写。玩这个,比玩 ASIC 要快很多,产品可以早点卖出去。另外如果有一天,你又想出了更好的玩法,你完全可以去把你已经卖给别人的东西换掉,变成更好的东西。而如果这个时候你面对的是你设计的ASIC,你只好哭吧。第三个原因,就好像广告里面说的:方~~~~~便。

再来看看这套玩具里面有些什么.首先是硬件,各种款式的 FPGA 芯片,各种应用的开发板。根据各自己不同的玩法,选择,拼接各种不同的产品系列。如果现成的不够用了,完全可以自己做一块。然后是工具。QuartusII,这是一个魔盒,它就好像 photoshop 一样,可以把你原本看上去有点尴尬的照片,变成美轮美奂的艺术品。NiosII,嵌入式 CPU,是的,你没听错,就是 CPU,而且是你想放多少就可以放多少的 CPU。还不过瘾?那看看 DSPbuilder 吧(这是我的至爱)。帮你把 Simulink 模型算法变成最终硬件的工具。是的,你完全不需要写一行代码。好了,不要摩拳擦掌了,开始玩吧。

关于 QuartusII 的一些事情
QuartusII 其实就是一个转换器。一个把你理解的逻辑语言转换成为器件能理解的语言,然后可以让 FPGA 按照你的想法去工作。我们写的那些 VHDL 啦,Verilog 啦什么的,其实都是人类自己定义的语言,对机器来说,就是对牛弹琴了,它没可能知道人类这些傻瓜坐在那里想做什么。所以为了交流,我们需要让他们理解我们的意图,而你也不至于因此而去学牛说话,所以,我们需要用 QuartusII,因为 QuartusII 就是帮助你进行这种翻译的工具。我们分两部分来完成这么一件事情,首先把你的逻辑思路转变成用已经有的元件搭建出来的电路。好比你说我要一个加法,器件没你那么聪敏,他不知道什么叫做加法,加法对他来说是没有意义的。工具会把加法转换成为一组逻辑,用与,或,与非,或非这些乱七八糟的连在一起,变成和加法结果一样的电路。这样器件一看就知道了,哦,对了,我有这些的。当然这个时候你再去看那些东西,可能就蒙了。这个过程就是综合。综合结果是一个网表文件(netlist),也就是一堆很无聊的电路。而这种电路还只是停留在概念上,并没有映射到实物上面。
然后工具会做第二件事情: fitting。QuartusII 把你选择的器件找出来,对照它拥有的资源来放刚才转成的电路。FPGA 里面的资源都是现成已经做好的,好处是你不需要做,坏处是,你也改不了。第一步首先是放置,就是把那些逻辑一个个放到器件的相应位置上。最后,把所有的放置好的点连接起来。这样,你的思想就在硬件上面完美的体现出来了。然后我们再回过来看看每个点都在做什么,把这些信息存成一个文件,以后你只需要每次告诉器件这个文件,就可以实现你的设计。
这一切都不需要你来做,工具都可以自动完成,因为对器件世界的理解,工具比你要熟悉的多,所以你可以相信,他一定比你做的好。但是这样是不是太容易了呢?当然不会,否则就不好玩了,你需要知道工具去实现,因为很多事情是工具不晓得的,你需要告诉工具你的要求来实现你的设计。这就是约束。好像女孩子喜欢漂亮,当然不会随便穿戴,要点缀这里,束缚那里,让自己看上去至少很美。打扮没有约束,可能顶多就是难看点。但是翻译过程没有约束,就完全不是一个东西了。所以,约束很重要,后果很严重。你要告诉工具很多信息。比如:时钟信息,工具不可能聪明到知道你从外面送进来的时钟是多少的。比如管脚信息,你要告诉他哪些输入,哪些是输出,而这些输入输出分别应该是什么样子的。这些是最基本的信息,还可以提出一些更苛刻的要求出来。比如说功耗。QuartusII 在完成编译以后会对编译的结果进行分析,看是不是能满足你的要求。如果不满足,它可以再试几次。最后告诉你:帅哥,按照你的要求,我们完成了工作了,或者说,小子,你的要求是在太过分了,我做不了。那么可能你得调整一下设计,代码,约束条件,甚至器件。

到此为止,其实 QuartusII 的工作已经可以说完成了。但如果只是这样,你一定会很郁闷,

因为你并不知道你的设计是不是真的可以工作。或者说,你会想知道你的设计为什么不工作。
所以必须提供仿真和 debug 工具。QuartusII 提供一个相对比较简单的仿真工具,让你可以通过画波形图的方式来做仿真。这种工具非常容易用,但是能力比较有限。他可以很直白的实现激励和观察输出波形的平台,但是他无法实现很好的交互能力。好比你需要因为一个信号而发动下一个动作时?你恐怕很难猜得出来什么时候这个信号就来了吧。所以很多时候,我们会比较喜欢用第三方工具 ModelSim. 这个东西不好的地方就是你需要自己写仿真代码。你会发现这是一件蛮痛苦的事情,很多时候仿真代码比电路代码本身还要复杂。但是,真的没有办法,我很同情你,因为只有你才知道你想做什么,没有人能代替你来写仿真。仿真毕竟只是仿真而已,不是真的,很多时候你会发现电路还是不能工作,虽然仿真看上去完全没问题。千万不要太自信的以为这一定就是工具的 bug,从而很兴高采烈的报告说,我找到你们一个 bug。绝大多数的情况下,要么是因为你的约束有问题,要么是因为你的仿真和真实情况不符合。所以,QuartusII 不得不提供一种途径,让你看到,芯片里面到底发生了一些什么事情,那就是在片 debug 工具,signaltapII。我知道名字不太好听,但是其实真的还蛮有用的。
QuartusII 就是这样一个工具,很简单,但要玩得好,还是需要一点功夫的。后面我会比较细化的聊聊怎么玩这个东西。我不会很老土的说怎么创建一个工具,然后按这里,按那里。这些要你自己去玩,去摸索,否则,完全没有乐趣了。

门规
别担心,这里不是黑帮。
我喜欢把寄存器比喻为门。所谓的电路就是由一堆这样的门包围着的。每个门都有一个相应的时钟来驱动,在相同间隔后,门将会打开。一些信号从一个门出来,经过一系列的变化(组合逻辑)变成一个新的信号,走到另一个门口,当门打开的时候,进入。这就是 FPGA 里面发生的一切。或许速度会快一些,或许情况会复杂一些,但是大体就是这么回事而已。而我们要保证的就是所有的信号都可以高高兴兴来,安安全全回去。不会迟到,也不会早退,更不会被门夹到,就万事大吉了。但是事情容易说不容易做,所以需要注意一些细节方面的问题。如果因为你把逻辑给做错了,那我们就不提了。而有些事情,即使你感觉一切都做得足够完美了,却还是没有结果。在这里提一些会有好处。

门规之组合电路篇
虽然我们要讨论的是门,但其实我们更关注的应该是门与门之间发生的一切。因为门的作用只是把阶段和另一个阶段分割开来,而更多的事情,其实是在这个阶段里面做的。门与门之间,或者门外面的,就都是这些组合电路了。组合电路最终影响了你的时钟能够飙到多快。

1. 进来的时候,请随手关门
这是一个再简单不过的规则。在进来的时候,请关一下门。相信如果你呆在一个有一部分没有被包围在墙和门里面的房子里,一定很没安全感。尤其是在门外面的东西吧,你会整夜整夜的睡不着觉。所以进来的时候,最好可以随手关门,把自己包围在一个安全的环境里面。所以信号也是这样,信号是很敏感很没安全感的东西。所以,进来的时候最好被时钟打一下。这在 FPGA 设计中是非常重要的,尤其是复位信号。外部信号的触发事件是随机的,连它自己都不晓得什么时候那个 reset 键就被按了一下,加上进来以后的线路会有一点延迟,恰好在时钟触发的时候,前面一个信号被触发了,后一个信号恰好被门夹到。就好像指挥抬手的时候,钢琴开始弹奏了,小提琴手还在摆 pose。 这样导致的结果就是大家不是同时开始工作。
门分很多种,材质不一样(时钟频率),大小不一样(时钟相位),还有很多属性不同(全局时钟?)。所以为了进入不同的区域,你必须关一下相应的门。这其实很容易理解,你进厨房,关厕所的门,就有点奇怪了。还是以复位信号为例,对所有你需要用到复位信号的时钟域,最好可以被相应的时钟打一下。提出这样一点,是因为它经常被忽略,而且发生问题的状况可能变成随机的。如果你的设计仿真没有问题,而在板子上发生一些随机的事件,那么很有可能是你的异步信号造成的。

2. 在门与门之间请不要来回走动
门(寄存器)与门之间是什么?是组合逻辑电路。信号的传递就是不断的打开一个门,然后进入另外一个门,这样一级一级的不断传递,你才可以一直从起点走到终点。在有些时候也许你并没有注意,你在到达另外一个门之前,你转了一个圈又回来了。虽然这只是一个简单的圈,但是这个动作会被无限循环,因为这个动作没有被时钟限制。所以导致的后果可想而知了。举一个最简单的例子:
D = A + B + C;
Reg C = D + B;
A 和 B 都是输入,而 C 是输出,你会发现其实这个 C 偷偷的回去转了一圈,而结果是使自己又多做了几次循环。而最终结果是多少要看这段逻辑自身的延迟和你的时钟频率,对不起,谁都不可能预测结果。而且每次编译的结果都会不同,因为编译导致的电路延迟是随机的。但是如果我们把 D 也用门关一下(寄存器),那么结果就会舒服了。
Reg D = A + B + C;
Reg C = D + B;
不妨在 QuartusII 里面尝试这两种电路。 工具会报告一个 combinational loop 的警告给你。也可以尝试做一些仿真体会一下。

3. 不要为了省钱,用假门 (Latch Vs Register)
让我们把这个门再好好的研究一下,首先是定义。我这里说的门更多是 flip-flop register. 作为现成的资源,它遍布于器件的四面八方。另外有一种叫 Latch 的东西,我把它叫做假门。
在普通数字电路中,人们会喜欢用到它。因为它的结构相对简单,所以资源上比较节约。所以我们需要说说这个假门的问题。

那么首先需要了解一下什么是假门,其实就是一个偷工减料的门。我们来看一个简单的图:

这确实是一个再简单不过的逻辑了,比 flip-flop 的那个寄存器要简单许多。当 gate 为高的时候,Q 变成 D,否则,就保持 Q 的值。很显然这是一个用电平来保存数据的一个逻辑,但是为什么我们不喜欢它呢?

1. 不稳定,和 flip-flop 做一个比较,register 是在开门的时候数据进去的,然后这个门马上就关掉了,所以数据只要在开门的前后保持稳定,那么从输出看都是可以保持一致的。但是 Latch 的结构中,当 Gate 为高的时候,Q 的数据会随着 D 的变化而变化。所以他无法保持一个稳定的状态。

2. 它给工具制造了麻烦。因为电路是由你设计的,工具并无法知道你的思路,所以他不会知道你想要的到底是什么时候的输入。因为你现在门是一直那么开着的。在这过程中,狐狸也来过了,狼也来过了,老鼠也来过了,工具不知道究竟以什么为标准来判断这个电路是不是能符合时序要求。这样的话,或许你会很幸运的在一颗片子上面完成任务。但是一旦换一颗,情况就变了。

3. 最后,作为一个精打细算的人,我们需要知道他是不是真的就省了那些传说中的资源了。大家不妨自己做一个 Latch 出来(QuartusII 的 megacore 中是可以找到 LPM_LATCH 的)。

真是不划算,你发现其实整个查找表都给用上了。而后面一个简简单单的 register 可怜巴巴的放在那里用不到。作为 FPGA 设计中比较独特的一个原则就是,这个世界不是你创造的,所以你必须去适应它,而不是顽固自己的意图。换句话说:坏人也是人,不是说你不能做,但都已经告诉你坏人不好了,你干嘛还要去做呢?留一个问题给大家去尝试,或者玩一下。什么状况下,或者说怎么写代码的时候,可以把Latch 写出来。4. 除了门,其他的一切过程都只是暂时的在 FPGA 中,除了门与门之间的延迟是固定的(时钟频率)外,一切的组合电路的延迟都是不确定的。你不能依靠一次编译的延迟结果来做你的设计,虽然这样看上去的效率会非常高。就好像接力跑一样,可以一棒一棒的传下去。但是电路是需要可以不断重复的一个过充。相信每次接力跑交棒的时间点都是不同的吧,你再做一次编译以后,你会发现整个世界都变了。所以在设计中,尽量避免使用这种手段。延迟链,经常会使用这种手段,比如几级非门来增加延迟。但是它也是具有不稳定性的,所以在不到万不得已的状况下,不要用它来增加认为的延迟。延迟链在 ASIC 中另外一种用途是增加扇出。而这在 FPGA 设计中是画蛇添足了。因为布线资源中已经加入了 buffer 了。记住这样一个规律就好了,凡是没有被门关过的信号都是不稳定的,都只是暂时的。

门规之时钟篇
门本身没什么复杂的地方,但是门的种类一多,麻烦就来了。而好玩的地方也来了。所以我们对门(主要是时钟)的选择就会有很多讲究。好像厨房应该配什么门,卧室配什么门?对于门的属性把握也影响到你知道工具工作的准确性。如果你给工具的信息本身就不是符合事实的话,那么结果一定是不堪的。所以,我们需要对门,提醒一些门规。

1. 再生门很多时候我们不得不用一些逻辑的结果作为时钟去驱动一个门,我们且管他叫作再生门吧,这种门所造成的问题是最多不过的,因为有一些先天不足。所以我们把力气主要多的放在这里。首先这些门的驱动时钟是由组合电路组成的,还记得之前的规则么?所有的组合电路都是不可信的。是的,往往有很多的毛刺啊,或者中间过程啊不可避免的出现,这当然不是我们想要的东西。所以,在生成新的再生门之前,你最好把这个时钟信号用原来的那种门在关一下。这样你获得的会是一个干净的,纯粹的时钟信号。

2. 门之相关再生门也可以分成两种,一种是锁相环所产生的时钟驱动的门。另一种是你用逻辑搭建出来的时钟。就好像有的门是工厂制作的,有的门是你自己劈木头造出来的,工厂造出来的驱动门会比较准确,效果比较好,可以省掉你很多麻烦,所以很多时候我们还是推荐用这种门。而且因为是工厂制造,所以工厂对这种驱动的属性,状态会非常清楚,所以在分析的时候,会自动获得很多信息,而不需要你来提供。但是有的时候,你或许不得不自己造一个门。那么这个时候你就需要比较细心的呵护一下。首先就是刚才说的毛刺问题,然后就是相关性问题。你需要告诉工具你造的这个门和原始的那个门的关系。好比时钟频率,相位。工具是很蠢的,它没可能了解高级生物的语言,所以你必须告诉它。所以再生门与原始门之相关性会非常重要 (derived clock )

3. 门锁 (gated clock)门当然应该可以上锁,就是可以把时钟停掉,那么门就不会被打开了。而最大的好处是什么?对了,就是省电。门不会一直开来关去的,信号也不会频繁的翻上坠下的。对芯片电力的贡献可是相当大了。但是同时也带来很多问题,主要是时钟的不稳定性增强了。最最简单的做法就是把门锁信号和时钟与一下。但是这样的组合电路通过了查找表以后的驱动能力降低了,导致了时钟从这一点到另一点的传输延迟变长了。另外你的门锁信号上面可能本身带有毛刺,那么可能反映到时钟上,那结果就不得了了。你可能不小心就被门夹扁了。针对新的一些器件,器件本身可以提供一些时钟控制模块,当你需要使用门锁的时候,尽量的使用这些模块,会让你的时钟变得安全(clock control block)。或许你会说,我并不是太在乎是不是省电,我只是希望这一块地方不需要因为新的数据而变化,等于是把功能停掉。那么这样的话,你可以不需要去控制你的时钟,而选择控制你的数据。让你的门锁来选择进去的是你刚才已经进去的信号,还是新的信号。想象一下,如果反复进去的都是刚才的信号,那么你的电路大部分情况下是不太会有变化的。这当然不是绝对的。但是这可以作为一种方法来避免不安全感。

当然总会有万不得已无奈的时候,好比老板脑袋被门夹到了。你不得不使用逻辑电路来锁门,而你的器件又没有特定的模块。那么最好的情况就是,你可以先用那个时钟,把你的门锁信号关一下。这样的好处就是可以把毛刺信号完全的规避在门外面,使你的时钟更加安全。

好了,好了,先就说这些好了,再说下去恐怕会因为门规太多,很多人要倒戈投降了。这些是比较容易被忽略的地方而已。如果还有什么补充,也可以一起交流一下。

你的 Q-zone,你做不了主

我的地盘我做主,这其实是一句鬼话。你很少真的有什么地盘你可以做主的,因为你很难作为规则制造者存在。你只有更好的依循规则,你才能更好的让事情按照你的想法去做。所以为了做主你的地盘,你最好依照一些规则,而不是按照自己的喜好来做,好比写代码。

上电初始值
在通常的状况下,所有的门在上电的时候输出为低。但是这并不是不能改变的。你可以把上电设置为高,这样综合工具可能会做两种事情,把输出反向,或者使用 preset 控制(如果存在的话)把初始值放进门里。
当时上电为高的做法,并不是非常必要,因为你其实是可以使用复位信号来获得你想要的初始状态的。
如果你觉得这是必须的,那么有几种方法你可以做:
z首先是在 QuartusII 里面你可以针对某个或者某些 门设置 power-up level 为高或低。
z在代码中使用 altera_attribute
z直接写代码设置初始值:
reg q = 1'b1;
always @ (posedge clk or posedge aclr)
begin
if (aclr)
q <= 1'b0;
else
q <= d;
end

门的次级管理信号

每个门都有一些次级的管理装置,好比清除信号啊,时钟使能信号啊。而这些装置当然都有他们自己的操作规律。如果你在写代码的时候可能适当的使用它们,那么综合的时候很容易就可以使得王八看到绿豆,大家都对上了。其实实现一个功能是没有问题的,但是如果你把功能按照它的自然规律来实现,从资源消耗还是很划算的。当然我知道大家现在都很有钱,不太在乎这些的,但是省吃俭用似乎还是硬件设计师德传统美德。你会发现年资越大的工程师在这方面越是注意,所以,如果你希望在别人眼里看上去比较牛的话,适当的使用这种手段,还是蛮炫的。
我们把这些信号按照优先级排列一下(不知道什么是优先级?找本字典先)
1. 异步清零信号 – aclr
2. 上电复位信号, - pre
3. 异步载入信号 – aload
4. 使能信号 – ena
5. 同步清零信号 – sclr
6. 同步载入信号 – sload
7. 数据输入信号 – data

我建议大家可以尝试自己用这些信号来做一个门出来看看,尝试怎么可以使用这些信号。然后使用 quartus 来编译验证自己的结果。这是一种非常有趣的玩法。首先,它可以使你对器件的结构更加了解,同时也锻炼了你写代码的能力。一旦两相结合,就可以牛的乱七八糟的。

双向信号
首先说明一件基本知识,在 FPGA 设计中,只有在输入输出上可以使用双向信号,双向信号是不能使用在内部逻辑上的。一定不要用这种信号,否则工具会综合出一个你都不知道会是什么东西的东西。

针对一个双向端口,你需要把它变成一个输入信号 in,一个输出信号:out, 和一个输出使能信号: output_enable. 所以代码其实很简单:
Assign birsignal = output_enable ? out: 1’bz;
Assign in = birsignal
这里有一个小小的提示,在写代码的时候突然不太清楚语法怎么写的时候,你可以在 quartus里面按一下右键,你可以发现一个 insert template…的选择。试试看吧。

加法器
加法器的做法就比较有趣了,这会涉及到器件本身的结构问题。相比大家都看到了,FPGA的单元结构是前面一个查找表结果,后面接一个门(寄存器)。我们当然希望可能尽可能的使用这些结构。

最好的状况是什么?就是使用前面的那个查找表做一个加法,然后直接送到后面的那个门里面,然后传到下一级做加法。这样说好像很没意思,我们举个例子来看看可以怎么玩:

比如我们要做这样一个加法 res = A + B + C + D + E

我们首先需要知道一下加法是怎么做出来的,加法本身其实是很简单的逻辑,但是问题是,你不是一个人在战斗,你需要有进位,你还需要送进位出去。但是你真的等到进位来了你才做计算,这样一个 100 位的加法,你大概要等到天黑了。所以,其实在实现一个加法的时候,我们会同时做两次,一次假设进位为一,一次假设进位为二。然后用前一级下来的进位来进行选择。所以用这种方式我们就可以理解怎么进一步在 FPGA 中实现加法了。

我们有两种查找表类型首先是 4 输入查找表 ( Stratix, Cyclone, 和其他那些老古董)。作为四输入的查找表,就比较适合两个数加,然后可以直接连到后面的门上去关一下。进位是通过进位链链接的。 所以这样我们可以做成这样的算法。

一号门 = A + B 二号门 = C + D.
三号门 = 一号门 + 二号门
四号门 = 三号门 + E

这样通过一个三层门的结构做一个五输入的加法器,来达到最好的效果。
6 输入查找表 (StratixII, CycloneII, 和其他那些比较新的器件)
在新的结构中,我们可以使用 6 输入的查找表,这样,就可以用三个数加在一起 变成:
一号门 = A + B + C
二号门 = 一号门 + D + E

这样就变成一种两重门的结构。
说这些,可以说是一种提醒吧。我们在写代码的时候就可以考虑硬件的结构,用这种方法,让你的实现可以变得更加专业起来。在 FPGA 中的硬件结构都已经是固定的,所以如果按照你的地盘的规律来写,那一定更完美。大家不妨尝试各种写代码的方式来实现。

状态机
状态机是设计过程中的核心部分,所以我们需要特别的提一下写状态机的一些注意事项。为了实现利益最大化,建议在 FPGA 中使用 one hot 模式的状态机,而在 CPLD 中使用最少比特数的状态机。在具体的设计中需要注意的是:

把状态机写全,也就是说不要漏写了 Default:。 没有这个首先会出现什么?对了,会有假门(latch)。

状态机作为控制核心部分,尽量把它和算法功能和数据分离开来。好像你看到好的流水线,控制流水线的电脑和流水线本身是分开的。这样可以保持相对的独立性。

如果一种操作设计到几个状态,尽量把操作剥离状态机本身。

使用一个简单的复位信号来定义上电状态。如果你的状态机会被比较多的复位信号复位的话,工具就不会把它当作状态机来对待。
总之,尽量的保持状态机的很傻很单纯是很重要的。尽量的不要加重核心部分的复杂性。其实道理很简单,好比在一个公司里面,真正在工作的,其实一定不是一个这个公司的核心。

小明和小强的故事

一直以来都在想写这么一个故事,这个故事的目的是为了说明清楚关于 timing 的一些概念。所以如果你对时序已经非常清楚的话,大可不必看下去以至于把自己反而搞糊涂了。有一天天气很好,所以小明决定去小强家里。小明和小强家的门都是自动门,不是你想开,就可以开了的,而是在一定的时间后打开,而这时间间隔是固定的。为了让时间变得简单一点,我们假设它是一个小时。于是,小明在自己家门 4:00 打开的时候出去了。然后他就向小强家里走过去,小强家的门是 5:00 打开的,小明在这之前到达小强家门口。这样小明就顺利到小强家里了。这个故事就讲完了。让我们重新把它推演一下,这个故事里面关键的是两点,一个就是小明到达小强家的时间,还有一个就是小强家的门的状况。这两个相对来说是两件独立事件。我们首先看一个小明从家里到小强家门口这么一个过程。首先是开门的时间点(Launch Edge),开门需要一点时间,你不能在它刚开的时候就出去吧(Tco)。最后就是从门口走到小强家门口的时间(Tdata),这个时间是不一定的,因为有很多方式可以到小强家里。我们会比较关心极端事件,就是最快的时间,和最慢的时间。这还没结束呢,最容易忽略的,其实就是时间的问题。好比小明以为他是四点点钟出门的,但其实他家里的钟其实晚了 10(Tclk1)分钟的。如果我们以一个标准时间来判断小强和小明家的钟。而他们家里的钟只有可能比标准时间要晚一些 ( 时钟传递到门的延迟 )。差不多可以了,我们看到这么一个公式

到达时间 = Launch Edge + Tclk1 + Tco + Tdata

如果非要给这些值赋一个固定的值,那么我来具体看一下。这里面 Launch Edge, Tco 都是固定的,我们假设他们为 Launch Edge = 4:00, Tco = 5。如果我们说小明花了半小时走过了中间的路,那么我们可以知道,当小明到小强家里的时候应该是 4:00 + 5 + 10 + 30 = 4:45。

好了,我们再来研究一下小强家的门。这个比较简单一点,就是小强家时间不准的时间,假如说为 5(Tclk2)分钟好了,再加上开门时间点 Latch edge。这其实说明门是晚开了 5 分钟。所以小强家开门的时间是:

开门时间 = 时间点 + Tclk2 + Latch Edge

这其实就是 5:05 了。我们一直在说小明要在小强家门开之前到,那么究竟多久之前到他就可以安全的进去呢?这里面有一个建立时间(Tsu)好比要通过安检啦什么的。把这个值设为 3 好了,所以其实小明应该在 5:02 (5:05 – 3)到是安全的。这点条件小明是符合了的(4:46)。但是还有一个条件。小强看到小明会很开心,所以他要和小明聊几句才让他进去,我们管这个叫做保持时间,在这段时间里面小明不能走了,走了的话,也就进不去门了。我们假设这个时间也是三分钟的话。就是说,从 5:02 到 5:08 之间,小明应该很安心,很稳定的呆在小强家门口他才能进去。小明进步了小强家可能不是什么大不了的事情,不过如果你的设计时序不合格,就要打屁股了。所以我们来看看什么情况下,小明进不了门。

首先我们俩安排几个参数。

Fmax:什么是最理想的状况,就是小明走最长的路到达小强家门口的时候,恰好在小强家门口的建立时间点上。而我们把这中间的门开的时间间隔的频率称为 Fmax.以这个为例:小明 4:46 分到了,如果小强家的门在 4:49 分开就是最好的时间点了。而小强家的钟的延迟有给了我们 5 分钟的冗余。所以最短的时间应该是 44 分钟。这就是 Fmax。 这也就是你的电路能够达到的最快的频率了。而我们规定的时间间隔大于 44 分钟就可以保证小明可以在建立时间点前到达。

我们给这个差异一个名字 setup slack。就是建立时间点减去小明的到达时间点。结果如果是正的,说明小明可以顺利到达,如果是负的,就不行。

刚才我们说过了,还有一个保持时间,我们来看看什么情况下保持时间会失败。我们给小明在找一个弟弟,这个弟弟呢会在下一次门开的时候出来,也来小强家。弟弟来的时候,小明就必须要离开了。如果我们把小强家的钟再搞晚一点,晚了 20 分钟的话,那么开门的时间就会晚到 5:25。而保持时间点是 5:28 分。弟弟从 5:00 + 10 + 1 +5 = 5:16 出发,假设他走了一条近路,很快的就到了,比如只花了 10 分钟,那么他在 5:26 分到了,小明就不得不走了。所以我们这里其实可以得到一个很有意思的结论,当发生保持时间问题的时候,往往是两种可能。

1. 相互之间的时间误差差距比较大
2. 中间的路径非常的短。

所以我们往往需要分析电路的最快表现来分析保持时间。这里面也有一个 hold time slack.就是小明弟弟到的时间点减去保持时间点。

这个故事差不多讲完了,大家可能已经晕了,其实我自己也差不多了。但是我们还要再接再厉。如果把两个门开关的时间间隔变得不同。做时序分析的时候,工具会找到所有状况中最糟糕的那个状况来分析。不用担心听上去好像很复杂,这都是工具自己会做的事情。你知需要规定你想要的间隔时间,工具就会为了你的目标而努力。 对这个故事的整个的了解让我们对电路的具体过程有更深的理解。让我们来看看我们可以做些什么来规避 Slack 出现负值的情况。

首先看看 setup slack:

1. 把时间间隔变大,也就是降低频率(这是废话)
2. 把路径缩短,就是把最长的那条路径变短,使用各种优化手段。
3. 有的时候,我们会发现,其实小明根本不认识小强,所以我们完全没必要管他是不是能进小强家门。(false path)我们可以告诉工具,不要去管它。
4. 如果在 5:00 到不了,6:00 到也可以,有的时候我们可能并不在乎他什么时候到,只要能到就好。我们可以设置为 multi-cycle.我们告诉工具说,没关系不用太着急的,下次开门的时候再到也可以的。

再看看 hold time slack:

1. 把时间误差变小。首先我们看看什么情况下可能导致小明家的钟差 5 分钟,而小强家却差 20 分钟。如果这个时钟是一个 gated clock,那么他的驱动能力是有线的,所以时钟传递的延迟会因此而变大。我们可以使用 PLL 来驱动时钟,这样的话驱动能力会加强,时钟传递的延迟因此而变短。

2. 把最短路径变长,这可以通过添加 LCell 来人为制造延迟

办公室的故事 ( Incremental Compilation)

我们总是在想,有没有那么一种可能,让我可以少花力气多赚钱,我们这里就开始讨论这样一个问题。

好比你现在有一个很大的公司,你有一个办公室,里面空空的,你可以有很多方法把你的员工放在里面。当然你可以一股脑儿的把所有人全部很随意的放在里面。但是导致的结果会是什么样的呢?你会发现两个工作关系很精密的人,每天需要从这头走到那头的交流。这样的工作效率不可能好的。所以我们会比较希望把他们整合在一起。至少可以让一个部门坐在一起。然后我们再在这个相对比较小的区域里面做调整来提高工作效率,这样会容易很多了。每个部门当然就是公司的一部分(design patition),我们需要把这个部门的人都放在一个限定的空间里面,而给每个部门提供的空间,我们定义为 Logic Lock. 空间是一个物理的概念,而部门是一个人为理念造成的一个概念。我们并不是一定要把他们俩结合起来。但是,如果结合起来会有这样一个好处。

好比,我有五个部门 A,B,C,D,E。而不给他们限制空间,他们可能随意的去做。也许相对来说部门内的人士坐在一起了,但是这中间留下的空间变得很没规律。两种情况下会有问题,一个是,需要加一个新的部门,那么这个部门就需要拆开放,另外就是某些部门扩张了,新人就没地方放了。所以,宏观调控是非常重要的。所以我们需要把设计理念的分割和物理空间的分割紧密的结合在一起。

增量编译的思想和这个故事是完全一致的。我们需要把设计分成一些相对比较大的部分,然后给他们安排相应的位置去放置。这样有非常多的好处:

1. 关系相对精密的电路的位置比较接近,减少了连接线上的延迟。

2. 对一些已经表现很好的模块可以保留他们的编译结果。这样可以获得最大的好处。每一次编译都获得一个比较好的结果加以保留,这样整个设计就一点点按照增量的方式获得了最好的那个结果。

3. 由于一些模块已经保留了,所以工具不会在那些模块上面耗费时间,所以编译时间大大缩短。

我们来看看有这种思想可以实现的两种编译过程:
自上而下:我是公司的总经理,我们去招了一堆人,然后把它们放在各个部门里面。根据每个部门人数的多少,给他们划分相应的资源。
自下而上:我是公司的总经理,我手下有一些部门的负责人,我让这些人去招人。我给他们一些人数的预算,然后给他们提供空间。那些部门经理自己去招人来填充自己的一亩三分地。最后把这个办公室填满。

和其他所有工具一样,我们需要有一些行为准则来获得最好的效果:
1. 尽量把工作关系精密的人放在同一个部门里面。部门和部门之间是不能进行优化的。好比两个人需要每小时都交流工作,但是你把他们放在两个部门,即使是相互隔壁的部门,也不能保证他们正好墙对墙的做。工具只对部门内部进行调整优化,出了部门,他们就死人不管了。
2. 关门原则。在部门和部门之间都按上门。就是说对输出和输入的数据都用 register 打一下。这其实是和第一条是相关的。因为部门之间不进行任何优化,为怕避免麻烦,建议大家都按上门来提高效率
3. 部门和部门之间尽量少串门。尽可能的减少互动。这个其实是可以在设计的过程中进行调整挑选的。
4. 保证一个部门的人数,不是太少(大于 2,000LE)。这很好理解,就那么几个人,还优化什么呀。
5. 不要放不用的接口。没有结果的连接在编译中是会被优化掉的。但是在增量编译中,却会被保留。所以如果已经知道一个接口是没有用的,不如把它干掉算了。
6. 大家知道,FPGA 内部是没有双向信号的,所以一定避免双向信号。除非你这个型号是要直接连到芯片管脚上的。
7. 避免同一个信号重复输出。
8. 不要把直接从输入到输出的信号放到部门内部。这是一种资源浪费
9. 公司有一些资源,好比打印机啊,传真机啊之类的,在分配办公区域的时候,尽量考虑资源的合理使用。由于资源位置是固定的,所以当你把区域分配给部门的时候,其实也把资源分配给他们使用了。而如果他们用不上,也就浪费了。
10. 多给部门与部门之间的交流留一些时间预算。这么看起来,如果你学会了增量编译,好像顺便把 HR 也个学了。

(本节的思维导图请下载附件)

8. 优化那些事儿
优化是一个很麻烦的问题,因为这个话题非常的杂,细碎。好像我们介绍一个人,你会一下子不知道从什么方面说比较好,因为他会同时拥有不同的身份,存在于社会当中。所以,我尽量的完成了这么一张图表,让大家对优化这个悬浮于平衡中间的一个游戏方法有一定的了解。平衡,其实是优化中的一个最关键的词。当然针对不同的应用,我们会有完全不同的需求,平衡点也会有算偏向。但是毕竟不能矫枉过正,太过苛求,否则只能是过犹不及了。这么说,似乎很没意思,让我们来说三个考量,时序,资源,功耗。这就是优化中的三个平衡极限。在有的设计中,算法对时序有要求,所以会要求设计能跑在一定的时钟频率上,这就需要对时序进行优化。有的公司在设计的后期开始考虑成本的问题,会希望选择尽量小的器件,那么这个时候,资源消耗变成了重点。而在手提式器件的设计中,功耗是至关重要的。而这三点,是没可能同时做到的。为了达到某种目的,你必须要付出其他的代价。所以,在做优化之前,做好一个优化目标是很有必要的。当然最基本的是时序,和资源。在这里我们比较重点的讨论这两方面的话题。 大家一定看到了前面这种让你晕得乱七八糟的图,我的任务,就是把他们解释一下。

(本节的思维导图请下载附件)

优化之前
在提优化之前,我们当然需要有一个提供优化的基本形态,就是你的设计。如果你的设计还没怎么完成,大可不必就着急的开始优化。因为每次编译都会把你的优化努力随机掉。而最好的优化方法,其实就是可以不优化。那就是把代码写的很优化,退而求其次,就是把代码写的容易优化。这里又要提老掉牙的事情了,代码要写得有层次化,好处就不罗嗦了。那么在写代码的时候需要考虑什么问题呢?首先是你使用的目标器件的资源状况。通过一些小实验,你可以知道,你写出来的代码,大概会实现成什么样。这对你写代码有一种感官上的映射非常有帮助。然后就是一些特殊零件的数目和位置,比如存储器(memory),计算器(DSP block),特殊的管脚资源(LVDS)。其实是把模块按照功能分割开来。从顶层电路看起来,真个设计就是一些功能模块的组合,看上去和规划的功能图一模一样。这样做的好处,自然是不言而喻的。也比较符合常规的美学思想。然后是状态机的问题,尽量不要写出太大的状态机,宁愿用一些小型的状态机来相互关联。(除非你希望不被老板替换掉而写出很炫的,不过那样你自己以后也会很麻烦,因为你的记性并不像你想像中那么可靠)工具会耗尽心机去实现你一个很宏伟的状态机,结果可能还不能让人足够满意。Glue logic. 就是两个模块之间的黏合逻辑。这或许是很难避免的。但他们往往会成为优化的一个盲点。这种三不管地区,最容易导致脏乱差等社会问题。所以,尽量把它们放进某个模块的势力范围。在知道了写代码的大概规则以后,我们来看看约束。约束本身并不是优化,而是给优化制定的一个目标。你需要工具达到什么样的目标,你当然尽可能的要告诉工具,否则让他这么放任自流下去,后果会很严重。所以最简单的,你必须要提供所有的时序信息。好比,所有的时钟频率,所有的管脚时序要求。这样 fitter 才会有一个着眼点,针对距离目标最远的路径去努力。时序报告也才有可能报告哪些路径是没有通过要求的。我还是推荐大家使用timequest 来做时序约束,好处是,它可能对你的时序约束和你的设计对照做分析,在做时序分析之前,先对你的约束做分析,然后告诉你,你有多少该做的事情而没有做的(为被约束的路径)还有多少你要求做的,而没有被做的(被忽略的时序要求)。这里提出一个话题,false path,有人叫假路,有人叫错路,都是英语惹得祸,我们还是叫它flase path。具体来说,就是不该存在的路,或者说,即使存在,也没人去走的路。那么对这种路,我们是可以让工具看都不用看的。关于 false path,其实倒不用太过着急。大可以在发生问题以后,再去看是不是。否则让你把每条 false path 挑出来,其实也是个蛮无聊的事情。对时钟的约束,要重点关注两个现象。首先是尽量少的在时钟路径上引入逻辑。这样认为的造成了时钟和时钟之间的 skew。我们都知道这不是什么好事情。另外就是一种上下沿都需要用来采集数据的时钟。对于时钟的约束有很多的地方需要注意,否则你的电路都不知道会飞到哪里去。不过这东西需要一些体验,靠嘴巴说是说不出感觉的。现在来说约束中最重要的一个关键,不要过约束。过约束的坏处一大堆,增加编译时间(你可能不太在乎),资源使用过度(可能也还可以忍受),导致其他的时序问题(那可是个大麻烦了)如果你对自己的约束有些不太放心,又或者说可能器件和器件之间会有很细微的差别,你可以给约束做一些余量,但是过约束是万万要不得的。

时序优化:

写了半天,终于开始写优化了,首先我们看看这个时序优化的问题。还是先看几个关键词吧。

优化目标:一个是全局的设置,告诉工具你这个设计的优化偏向。当时序优化偏重要的时候,可以设置为 Speed。当然这使以牺牲 area, power 为代价的。也可以对某个独立模块做局部设置,就是说在这个模块中,优化目标是时序。

网表再综合( netlist re-synthesis): QuarutsII 支持一些第三方的综合工具(说实话,Quaruts 自己的综合工具,也还是可以的)。他们把代码变成可以映射到 FPGA 上的网表文件,就是由一堆查找表和寄存器组成的东西。再综合会把这些网表还原成为与非门结构的电路,然后再根据 quartus 综合工具的算法再做综合。这当然未必是一件好事,可能会因此而破坏了原来工具中一些好的算法做出来的逻辑而导致结果更差。

物理综合:这是时序工具中效果最明显的工具。它对 fitter 以后的结果,在关键路径上,对电路进行调整。既然布局布线都做好了,还有什么可以做的呢?有很多。首先是组合电路。组合电路,无非就是一堆查找表的连接。而布局布线本身是一个比较随机的过程。在完成以后,我们会发现一些路径是比较差的,那么我们可以对这些路径做一些调整,他们选择更短的一条路径,同时功能保持不变。对异步控制信号做一级同步流水线。这样可以规避一些recovery 和 removal 的时序问题。还有一个功能是复制寄存器,比如说一个寄存器的 fanout比较多,或者说,其中的一条线会连到比较远的地方。那么我们可以在那个比较远的地方复制一个和这个寄存器完全一样的寄存器,这样可以大大提高时序效果。当然这需要牺牲一个寄存器了。还有一个好玩的事情,是寄存器的 re-timing,如果我们把寄存器看成流水线中的一层的话,它其实就是位于一条路径中的某个点而已。他的前后两个组合电路的延迟可能是不同的。而可能恰巧那个长的变成了一个关键路径(就是大大的坏的一条),那么我们是不是可以移动一下这个寄存器的位置,让大家的时间可以平衡一下,而结果是时序上去了。但是在功能上会有一点小小的变化,所以这个选项还是慎用。

可能造成的几种时序问题:时钟路径导致问题,这个时钟可能是个 gated clock,或者非全局时钟。这个调整的余地相对比较小。

数据路径导致问题,这个比较复杂,首先可能是 Fan-out 太大导致的延迟过大。也有可能是逻辑门的层数过多导致。或者布局布线的随机随得不好,两个点的距离很大。还有就是一些物理限制,比如 DSP block 之间的相对位置。

用来观察时序问题的工具:

Technology map viewer: 这东西,实在是没办法用中文说了,我私自反了,恐怕你会恨死我,因为在 Quartus 里面死活找不到了。它是可以用来观察你的综合结果以后的电路实现的。所以用它,你可以看到你的电路经过了多少层次的路径。

Chip planner: 这是用来看你的电路具体在芯片上的实现的,通过它你可以观察到相对之间的位置的距离,和连线上的延迟。

TimeQuest: 这是时序分析的最佳工具,无论是观察时钟或者路径延迟,fanout, 或者逻辑层次,都可以使用它。虽然不一定比其他的工具更直观,但是它是最全面的。

针对一些经典的可能性,表格里面列出了一些,大家可以自己看一下。


资源优化
和时序优化相对的,我们可以给 Quartus 一个优化指导方向,当你把它设置为 area 的时候,工具会尽量考虑资源的使用。可以做全局设置,也可以做局部设置。

Register packing: 这是一个针对 LE 资源优化的工具,在新的 Quartus 中一共有七级设置。比如把两个互不相干的电路挤到同一个 LE 中。根据等级设置的不同,会有不同的资源消耗结果,当然付出的代价就是时序的降低。

当然对特定模块的使用,是需要一个整体规划的。什么样的状况下去使用存储器资源,什么样的状况下使用 DSP-block,都需要有一定的评估。更多的时候,其实在写代码的时候就已经做好了计划。

帮手:相信大家已经有点晕了,幸运的时候,我们不是一个人在战斗。我们还是有很多帮手的。让我们来看看这些家伙:

Design assistant: 这是一个会提供给你一些提示的助手工具。他会显示出设计中会导致编译结果的地方。你可以自己定制一些设计规则,他会帮助你去监督你自己,尤其在一个团队设计中,对大家设计的同一性有比较好的效果。

Optimization advisor: 优化指导,会提出一些相关的优化选项的指导。当然这种指导是非常通用的,不可能非常针对你自己的应用,所以在是否使用这些建议的时候,还是要自己权衡一下。所以简单的了解一些优化选项,还是有必要的。否则,莫名其妙的就被工具耍了。

Early timing estimate: 这个工具会做一些最基本的布局布线,这样它可以很快的得到一些时序方面的信息,这在你做时序约束,和logic lock设置的时候有很多的指导意义,同时也节省了一些时间。

Seeds: 谁都靠不上,我们靠老天。布局布线基本上是一个随机的事情,没有人能够了解结果能使什么样子的。而seeds就好像一个赌博用的骰子一样,告诉编译从什么状况开始布局。没有人知道seeds 中的具体某个数字代表什么样的状况,所以就是不断的碰运气吧。给大家一个迷信,似乎17这个数字特别好,结果会比较理想一些。

Design Space Explorer: 这个是一个实在没办法的办法,但凡还有一线生路,最好不要用这个东西。他会自动的对你的设计根据一些设置,不断的尝试布局布线。比如使用不同的优化方法,不同的算法,甚至不同的种子来做编译,最后给你一个最符合约束要求的编译结果。但是这样的工作量有多大,大家可想而知了。如果你有比较多的时间,你可以试试把它扔在那里跑个几天。

Handbook: 啥也不用说了。

我想用一种比较有效的方式来描述关于系统设计的一些事情。设计是没有一个固定规则的过程,否则也就没有乐趣可言了。虽然有很多标准,有很多限制,但是依然有非常多的变化空间。其实最重要的事情,是知道什么是重要的,什么是次要的。精力应该集中在重要的东西上面,而不是一些细节。做系统设计需要有一种大的气魄,一些不拘小节的气质,当然,还需要别出心裁的创意。FPGA 本身提供了一个非常灵活的平台,如何最大化的利用这个平台,是我们脱离呆板的嵌入式系统的一个重要关键,也是如何玩转系统的关键。

不求甚解之 NiosII
所有的系统都是由模块组成的,或大或小的模块,拼接成一个大积木。所以我们首先需要了解这些模块 (IP)。关于怎么去了解一个 IP,其实是很重要的问题。NiosII CPU 作为一个比较大型的模块,可以作为一个例子来讲。一个关键词是不求甚解。不求甚解的目的,并不是偷懒,而是更准确,更有针对性,更快捷。IP 的作用就是为了完成一个特定的功能,所以我们并不需要知道它是如何实现的,事实上,由于很多的 IP 都是加密的代码,所以也不可能知道具体的电路状态。同时也不需要花很多的时间把文档里面的每一行都了解清楚。作为工程师,大家的脾气一般都是一种超强的好奇心和钻研精神的集合。而往往会钻进牛角尖里面。而作为系统设计,是需要有一种粗旷型的大气魄,不需要在细节上浪费时间。你会发现很多的细节是没有意义的。并不是说我们不需要去研究细节,细节是很重要的,但是细节需要在被用到的时候才去关注就好了。

作为一个 IP,最重要的,其实是接口。因为你最重要的是需要知道是怎么让它工作起来,而不是它怎么工作的。所以在看文档的时候,最主要看的就是接口信号,对所有的信号的作用有一个了解。NiosII 使用的是 Avalon MM 点对点接口,这其实是一个非常有趣的接口,因为它的交流更加短平快一些。它与普通的 PCI 接口不同的地方是,他可以支持同时多线控制。因为它没有总线的概念,不会在总线被占据的时候,其他任何通讯都无法进行。NiosII是在 SOPC builder 中被直接使用的,我们不需要知道具体有哪些信号,因为没有非常需要,我们是看不到这些接口的。在 NiosII 中,我们有两个 Master Avalon MM 接口,一个是Instruction Master Port, 这是 CPU 用来读取指令的接口。CPU 通过这个端口从 Memory 上读取指令。另一个是 Data master port, 很简单,这是用来连接数据通道的。比如说你要读取的数据,你要存储的数据,都是走这个通道。这两个端口可以连接同一个内存,在这种时候需要特别小心,很有可能自己把自己的指令给改掉了。但是反过来思考一下,其实我们可以做什么?可以按照状况改变软件代码。NiosII 中还有第三个端口,这是用来做 Debug 用的端口。还有其他的一些接口,比如 TCM 接口。我们需要知道这些接口的存在,但是不需要知道细节,只有在用到的时候再去看相关的文档就好了。

第二个需要关注的问题就是参数设置。这里面是有讲究的。IP 是厂家做出来的通用模块,不是为你而特制的,所以必然有一些是你不需要的方面。我们可以通过参数的修改,让它尽量的接近我们的需求。有很多人在做设计的时候是有思维定势的,而且这种定势的顽固性很强。这很容易对环境产生一种叛逆思想。就是说除了他自己假想出来的做法,其他的一切都是不对的,或者说不好的。而这在使用 IP 的时候,会遭遇到意想不到的痛苦的。所以,尽量不要依靠假设来臆想了模块的设置。而是尽量的适应环境,来配置自己的设计。作为一个FPGA 的玩家,这种依照环境来改变的能力是必须的。

NiosII 的参数中首先是指令集或者说 CPU 复杂度的选择,有三个选择,根据不同的选择,CPU 的能力会有不同,当然使用的资源也是完全不一样的 NiosII/E (经济型)能力最弱,当然资源也最小(600-700 LE )
NiosII/S (中间型) 中庸配置,资源消耗是 1200-1400 LEzNiosII/F (快速型) 能力最强配置,资源消耗是 1400-1800LE

然后我们考虑 Cashes 的设置,Cash 有两种,一种是用来做指令缓存的,一种是用来做数据缓存的。Cash 的大小对程序的运行速度是有影响的。当然也没必要使用过多的资源。够用就好了。

再了解一下 Jtag Debug 的模式选择,一共有五个等级的选择。和选择 CPU 一样,从简单到复杂。一般来说选择 Level2 也就够用的。

自定义指令设置。这是最有价值的设置。别忘记了,我们是在 FPGA 的世界里。所以 CPU并不像在其他地方那样的铁板一块。我们可以选择使用自定义的指令。所谓自定义指令,并不是一个软件宏或者函数。而是一块硬件。当 CPU 调用到这个指令的时候,事实上它调用的就是这个硬件模块,它被嵌入在 CPU 中。而这其实就是 NiosII 好玩的地方。

好了,现在我们要考虑的问题,就是使用。使用 CPU 的方法,当然就是软件编程。NiosII的软件其实是非常简单的。就是普通的 C,或者 C++,需要做的就是不断的对端口的地址读啊,写啊,计算数据,就好了。但也可能非常的复杂,因为你不仅需要了解软件编程,你更需要了解你使用的那些硬件,那些外设模块。NiosII 的编程很硬件的依赖性是很强的。针对比较大型的一些外设,可以写一些 HAL 程序。这是类似于驱动的一些指令。而软件只需要调用这些 API 就可以了。大部分的 NiosII 程序是不需要使用操作系统的,作为一个嵌入式系统的控制核心,更多的是一些存储式的读写,算法的计算的操作。除非你需要运行一些网络协议啊,什么的。但其实我们可以用更加解构的方式来看待一个操作系统。操作系统其实就是给我们提供了一大堆的操作指令而已。没什么更特别的作用了。所以,思考一下吧。

我用了不求甚解的方式来介绍了一下 NiosII。这是一种偷懒的方式。我要做的,其实就是把一些关键的点指出来,大家可以去看,同时不把时间消耗在细枝末节上面。要把 NiosII 完整的说清楚,当然不是这么三言两语的就可以的了,否则事情也太简单了些了。我希望可以对大家有所帮助的地方就是,对一件复杂的事情,找一个聪明的方式。

囫囵吞枣之 SOPC

SOPC builder 是 QuartusII 中用来建立,开发,维护系统的平台。虽然很多时候我们用它作为 NiosII 的一个嵌入式系统的开发环境,但我希望大家不要把思路只是局限在 NiosII 上面。它将成为一个包容全部系统内容的一个平台。

让我们把一个系统分割成为两个部分。一个是控制部分。所谓的控制,就是对很多的寄存器进行读写,或者是对内存存储器进行操作。而这些操作往往是与地址相关的。所以,我们使用 Avalon-MM 进行这部分的连接。好像很多的系统一样,这个接口的两端是主从设备。主设备发出要求,从设备被动接受操作。举个简单的例子,CPU 永远都是一个主设备,而一些存储器,比如 Flash, SRAM 则是从设备。而 CPU 并不是唯一的主设备,比如 DMA 也可以作为主设备。由于 Avalon-MM 总线的点对点结构,当 DMA 对一个从设备进行处理的时候,CPU 可以对另一个设备进行出来。这就是这种结构最大的优势。

但是单一的 Avalon-MM 并不能完全的解决问题。因为对于某些传输而言,地址是没有意义的。而高的通过率可能更重要。这就是数据通道。一些数据的处理,比如滤波,视频处理的操作。而这就需要用 Avalon-ST 的接口进行连接。在这种结构中,接口的两端是相对平衡的地位。作为 SOPC builder 中的一个模块,他可以同时拥有多个所有的端口。然后再端口和端口之间进行连接。

………………
………限于篇幅这部分请下载附件………
………………




大家一起来搭积木好了。

啥是 DSP
终于写到 DSP 这部分了,由于是我的本行,我反而一下子不知道该怎么写好了。或许最主要的一个原因是 DSP 涉及的范围实在是广得乱七八糟了点。突然之间变得有点无所适从起来。所以只好暂且写到哪里看哪里了。

大家知道我们这里说玩转 FPGA 的,每次我在 FPGA 里面提到了 DSP 的题目的时候,总是会有一些人打断我说,兄弟啊,你大概走错地方了。我们这里都是硬件工程师,或者可能有些写 C 的,但是 DSP,那东西不会写哦。那么好吧,我就跑到 DSP 那里去讲,FPGA,然后就会有人问我,你们这个 FPGA 和 TI 的 DSP 比有什么区别呢?我晕。我想说的是,我这里的 DSP 是 Digital Signal Processing (数字信号处理),而不是 Digital Signal Processor (数字信号处理器)。

不能否认处理器在数字信号的历史上拥有绝对的贡献和近乎垄断的地位。作为最简便的一种实现方式(软件),数字信号处理器具备非常大的灵活性,以及对系统的升级能力。但是,随着技术越来越复杂高端(很多其实是蛮没必要的),处理器开始不断的在它的瓶颈上碰壁。一个是算法的复杂度,一个是对多通道的苛刻要求,使得产品对处理速度的要求几乎成指数级增长。这就使得 FPGA 有了施展拳脚的地方。是的 FPGA 就是硬件,或许那里面的一个乘法器无法和处理器里面的乘法器相比,但是架不住人多吧。我用 100 个,两百个,四百个同时运算呢?所以我们开始观察这个市场的时候,FPGA 已经成为无法被忽略的一部分。再反过来看看 FPGA,那是什么玩意儿啊,一个只能用来做数字电路的芯片,不用它做数字信号处理,还真想不太出,它更适合做什么呀。

好了,让我们重新考虑一下 DSP。DSP 的观念其实是非常简单的,它就是一个转换器。好像你从这里进去,从那里出来,出来的时候你就不是你自己了,要么瘦了,要么胖了,甚至可能说性别都变了,或者可能说别人的脑袋换到你的头上去了。总之,作为数据,你从这里进去,经过这么个玩意儿以后,变成一套新的数据。这就是 DSP。一定很多人说,你这不是废话,要是都一样的,要你芯片做什么?还真的,想要在数字电路里面脱离 DSP 真不是那么容易的事情了。当然也不能把话说得满了,否则很多人要不高兴了。好比网络协议处理,就不是 DSP,因为一般会关注的都是包头信息,而对包本身的内容,从来都不关心。而 DSP关注的是要改变数据本身的。让我们说得更加具体一点好了,比如通讯,尤其是无线通讯。通讯嘛,很简单就是要把信号送出去吧,但是数字信号不能就这么送出去,除非有根线。所以要对数字信号做一些改变,比如说调制,把信号变成什么 BPSK, QPSK,QAM 什么的。有了调制,在接受端,当然就要有解调。这就是我们俗称的调制解调器。我们这里来玩一下英语,调制:Modulation,解调:Demodulation,所以调制解调器就叫做 Modem。另一个比较大的应用就是图像处理,图像那么大的数据,没办法不变化的就用,所以要对图像进行编码,当然在播放的时候,需要解码出来,编码:code,解码:decoder,所以编解码器我们叫做:codec。通过这些例子可以发现,DSP 一般都是一对的,这很容易理解,对数据进行变化,并不是因为我们吃饱了撑的没事情做,而是有一定的目的的。比如为了节省带宽,我们需要对数据进行压缩。比如由于接受端的误码率会比较高,所以我们需要用纠错算法,为了达到纠错的目的,我们就必须对数据进行卷积编码。而接受端就要做解卷积纠错。所以在对数据进行变化以后,我们通常会在另一端把它恢复出来。否则谁知道那是什么东西呢。这也可以当作是 DSP 的一个特点吧。

完全没必要把 DSP 看成多么复杂的事情,算法,听上去多么高深的东西呀。其实只要是所有的学过加减乘除就可以自己做算法。而所有的算法都是有一个目的的,只要你理解了这个目的,至于说具体的方法,是可以仁者见仁智者见智了。举个例子,看是不是真的超出你的智商了。传统电视,或者说摄像的时候,我们是使用隔行扫描的方式。就是说每幅图片其实只有一半的数据,比如我第一次给你 1,3,5,7,9 行,第二次给你 2,4,6,8,10 行。因为发现人的眼睛是很容易骗的,所以用这种方式你其实是察觉不出来的。但是我们现在用数字电视了,而且要高清,所以你不能那么忽悠我啊,为了省带宽,可以少一半,但是放出来的时候不能那样。所以我们要把这半幅图在变成一幅完整的图,这就变成了逐行扫描。你想象一下可以用什么办法做呢?想一下。答对了,两种方法,第一种方法是,可以直接把 1,3,5,7,9 copy 一下不就好了?这种模式叫 Bob 模式。还有一种方式,就是等到 2,4,6,8,10 也来了,再把它们两拼起来变成一副图,放两次,这种模式叫做 Weave 模式。Weave 模式的好处是图像看上去更加清晰。简单吧?但是有一个问题,毕竟这是两帧不一样的图片,是在不同时间点拍的。所以当一个东西的移动速度特别快的时候,你会发现两幅图片是有细微差别的。这就会在电视上看到一些锯齿状的边缘。如果大家仔细看自己的电视,是可以发现的,尤其是很多广告里面。那么为了解决这种问题,我们可以比较一下他们之间的差别,如果差别超过了一定的值,我们就用Bob 模式,如果没超过,就用 Weave 模式。你看,简单吧,其实算法就是这么回事情,没什么大不了的。做 DSP 需要有一种无招胜有招的精神。因为 DSP 的算法实在太多了,你完全没可能记住,也完全没必要一个个的去学。只有当你需要实现的时候再去学习就好了,方法就是首先知道算法的自然原理,很容易就可以理解算法原理。而算法原理也不过就是一些加减乘而已,连除都很少的。

DSP builder 是干啥的?

DSPbuilder 就是 Altera 设计用来方便客户设计 DSP 的工具。首先我们了解一个背景,现在最牛的算法软件毋庸置疑的就是 Matlab 了。或许在统计学,或者其他的数学模型方面有更加专业的工具,但是对于 DSP 而言,Matlab 是不二的选择。N 多的人在用他来搭建模型。我们可以把 Matlab 分成 matlab 和 simulink 两部分。Matlab 更多的是对数组进行一系列的计算,而这种计算是静态的。而 simulink 是使用模块化的方式来搭建一个模型,而这个模型才是动态化的。当我们用 matlab 做成一个算法的时候,先不用开心太早,因为它完全有可能是无法实现的。更多的只是一种理论上的公式。只有当我们用 simulink 来搭建出一个模型的时候,我们可以知道,它是可以被实现出来的,无论是软件,或者硬件的方式。

我们小的时候学数学,学乘法,大家一定还有美好的回忆,个位乘,然后进位到十位,然后在一步一步的算下去,算了长长的一溜,很有成就感的。我记得那个时候我们还要用尺来画中间的那根线呢,因为老师说那条线要画得直,好像画不直的话就算不出正确答案了(大概所谓心诚则灵的意思吧)。后来知道了,这个世界上,居然有一个东西叫做计算器。那个东西是你无论如何光着脚都撵不上的速度,而且不会出错(不过好像也见过 1+1=3 的计算器)。我们于是发现自己似乎被骗了,我们为什么要做那一长串的中间过程呢?是的。如果我们用这种方式来考虑问题的话,一边是做好的算法模型,一边是我们的 FPGA 芯片。为什么我们还需要很辛苦的写什么 Verilog, VHDL 代码呢?就好像在学 Verilog, VHDL 之前我们做什么?我们学数字电路,现在还有几个人在用那些与非门搭电路呢?所谓社会的进步一定是不断从低端向高端发展的历史过程。所以,与非们对于 Verilog 来说是没必要的中间过程。但其实 Verilog 本身又何尝不是呢?当然了,Verilog, 与非门这些东西,对于我们对电路的理解和学习是做出过贡献的,但是,是不是到了应该把它们淘汰了的时候了呢(我这里说的只是和算法相关的电路)?或许现在还没有真的到了这个时期,但是它正在一步步的向我们靠近。而 DSP builder 就是基于这种思想的工具。让我们看看用了 DSP builder,可以给我们省多少东西。

首先是省了时间,时间是这个功利社会最重要的东西,没有人有耐心等待你做一个十全十美的东西出来。谁最早出来,哪怕是东西烂到不能用,都是成功的。所以时间比什么都重要。然后是省了人,不需要找一些人来把那些算法转变成为代码了。如果大家对 Simulink 比较熟悉的话,可以看到,simulink 的库里面有你的一切实验环境。好比信号发生器啊,信道啊。另外还有最全面的检测系统,比如说示波器啊,频谱分析仪啊,误码率分析器啊。有了这一切,你还要实验室做什么?至少你不需要那一大堆昂贵的仪器了吧。

物尽其用,人尽其能,我们还是希望尽量多的去发挥一些优势性的东西。所以千万不要说用DSP builder 去做一个非常复杂的控制逻辑,那只能是得不偿失。所以,我更建议大家用 DSPbuilder 来做一些算法方面的实现。然后作为整个系统中的一个模块来使用。这样可以使你的设计更有效率,并且可修改性增强。DSP builder 和 SOPC builder 的结合使用可以大大增强你的设计能力。比如说,我们可以用 DSP builder 来做一些运算模块,然后再 SOPC builder里面连接起来。再用一个 NiosII 来作为控制,写一些软件代码。一个看上去非常复杂的系统,其实已经可以看到了。而这整个过程中,我们没有写过一行硬件代码。可以说,这种系统设计思路就是未来的一个趋势。

DSP builder 设计看上去简单,但要精通也并不容易。有两个方面是比较让人困惑不已的。如果你是一个做算法的人,你对 Simulink 非常熟悉,而你对硬件方面所知寥寥,那有可能犯第一个问题:时序问题。在设计中,你很难找到时序的节点,就是寄存器,因为寄存器都是存在于模块本身里面的。有很多人设计了一个从头到尾都是组合电路的设计。而这种设计是肯定无法通过时序验证的。解决这样的问题,现在有两个模块可以帮助。第一个是 8.0 里面新出来的,Display Pipeline Depth。很明显,这个模块的作用就是现实所有模块的流水级数,就是这个模块里面有几层寄存器。这样你从模型上就可以很清楚的看到两级寄存器之间的距离,规避冗长的组合电路。另外一个就是 Resource Usage. 这个是资源使用显示模块,在编译以后,可以显示资源的使用量。但是它有一个附属功能,其实更加有用,就是 timing。它可以用来显示所有没有通过时序分析的路径,然后在模型中间 highlight 出来。这样可以帮助你很容易的发现问题所在,然后做些亡羊补牢的事情。

针对对硬件非常熟悉,但是对 Simulink 这种东西非常不熟悉的人有一个麻烦,那就是时钟。在 DSPbuilder 的设计中,所有的时钟和时钟复位信号都是隐藏的。在 simulink 中,时钟是通过采样率来定义的,或者叫时钟域。从一个初始的采样率开始,后面的模块都是使用相同的时钟域的。我们可以使用 Rate Change 目录下的模块来对时钟进行切换。而这似乎让人很容易晕。这里我们可以使用 simulink 的工具。它可以设置使得不同时钟域的模块显示成为不同颜色。这样看起来就舒服很多了。

再说一个 DSP builder 里面最让人心动的东西吧。HIL ( hardware in loop)。这是一个非常有趣的概念。我们现在讨论的是一个 FPGA 平台,这个芯片是可以无限次烧录的。不会烧一次收你一块钱的。而我们在做设计的时候经常有这样的问题,对设计本身没有把握,不知道是不是对,当然可以做仿真。但是仿真有的时候很麻烦,一个是速度可能会太慢了点。一个是我依然不相信你们的这个仿真模型是不是真的够仿真。所以,你就可以尝试用 HIL 来加速你的仿真,同时增强你的信心。HIL 把你的设计包裹在一套接口中间,进行编译,然后下载到板子的 FPGA 中间。Simulink 通过下载电缆把测试数据不断的灌入,然后在输出端不断的获得硬件跑出来的结果。通过这么一个过程,保证你的仿真达到多快好省的境界。但是,这也只是仿真而已,我不管它叫做测试,因为它不是在真实的时钟频率下操作的。时钟是通过Jtag 的时钟驱动的。所以你不要指望它可以跑在 100,200 兆上。但是速度依然已经够快了。如果这样的电路下载下去还有问题,那可能性只有一个,你的约束错了。或者是时序有问题,或者是你的管脚出问题。而你的算法设计本身不会有问题。

咋还有个 Advanced 呢?
为啥又出来了这么一个叫做 DSP builder 高级模块组呢?(8.0 里面才有)。让我们首先来看看DSP builder 本身。有这么几个问题。首先 DSP builder,号称是用来做算法的,但是搭出来的模型看上去更像是电路图,和本身算法的框图区别巨大。其次,作为一个不太了解硬件的人,可能我并不知道如何才能达到我需要的能力。换句话说,我的时钟是 100 兆的,但我并不知道怎么样的电路就可以做成 100M 的。还有一些细节上的问题,比如说多通道的问题,比如说系统层面的问题。所以,我们需要一套更加强大,有扩展性的平台来解决这些算法设计上的核心问题。我们来看看这个高级模块的四大特点:

1. 多通道支持,在这个模块组中,接口都异常的简单,基本上就是这样三个,V, D, C. V就是 Valid, D 就是 Data, C 就是 Channel。所以要告诉他的就是,是个数据,是不是有效数据,是那个通道上的有效数据。所以,无论你是多少通道的设计,无论你怎么修改你的通道数目,模型就还是这么个模型,都是一样的。这样可以使你的模型和你的算法框图看上去几乎是一样的。

2. 自动插流水。这个是比较高级的一个功能。就是在设计中间自己加入寄存器。你不需要在设计里放任何一个寄存器。你只需要告诉工具,你想要的时钟频率,和你的目标器件,工具可以自己在电路中间插入流水寄存器。这样可以保证你的设计完全使用器件的最大能力,同时不会出现时序问题。可能的缺点就是,你无法预知延时,而大家知道,如果一个设计是流水线模式的,其实延时是多少并不重要了。

3. 系统层面的设计。这也是一个比较新鲜的东西。所有设计里面的寄存器都会被编入一个系统地址查找表,比如说 FIR 的系数,一些控制寄存器都会有不同的地址。我们可以通过一个系统接口来对这些寄存器进行操作。这样使整个设计更具有系统化概念。在编译的时候,同时非常高级的生成一个寄存器列表(网页格式),包括寄存器名字,地址,初始值。所以可以见到,通过这个高级的模块的增强,使得算法方面的实现与设计变得更加容易。也可以很容易的实现非常复杂的系统。


后记

花了点时间,写了一大堆自己都看不太过去的东西。也不知道是不是把人都看得烦了。总是很容易的就陷入了技术的陷阱里面,而无法到达自己定义的娱乐的境界。很有点惭愧的味道。看来再不能自诩为芯片业中娱乐能力最强的人了。大概只能算是其中之一了。不过想想也没太必要牵强于一种观念,不能娱乐就不能娱乐罢,关键是能不能因此而有一些收获。如果说看了这么多,有了很多收获,有一些观念就这么莫名其妙的建立起来了。那这些别字百出的文字也就有他们存在的意义了。这些文字到这里算是一个断点,当然我也并不希望就是一次结束。希望可以成为一种开始,一种对技术讨论的新的方式的尝试。我并不能确定这种尝试的意义具体能有多重大,因为我能想到的就是我自己床边桌前那一小块地皮上的鸡毛蒜皮。但是无论如何,这算件挺有意思的事情。而我本身也算是在自娱自乐了吧。

但是,问题是,我依然是盲目的。我不知道看我的这些文字的人在想些什么,他们具体的需要什么,他们想知道什么。我只是摸着脑袋猜,阿门,他们大概应该知道这些吧,于是我就写了。这当然肯定是不精准的。我也并不太想去写那些文档中已经有的东西,所以一些教科书式样的文字在我这里是没有的,虽然那样可以消耗很多篇幅,比如说,第一步,第二步。拜托,大家都是成年人了,能不能有点创意呀?完蛋了,又跑题了。所以呢,其实我想说的是,我需要反馈,我需要反馈来让我做的更好。我不希望我只是一个孤独的行走的人,我希望看到有多少人在看着我行走。

所以,如果你,可以给我一些你的想法,你的需求,或者你的问题,哪怕你的理想。都可以一股脑的灌给我。垃圾邮件暂时还是免了。

祝大家玩得开心。

完整的pdf格式文档51黑下载地址(共52页):
大话FPGA.pdf (3.29 MB, 下载次数: 288)


评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏17 分享淘帖 顶4 踩
回复

使用道具 举报

沙发
ID:621747 发表于 2019-10-10 15:21 | 只看该作者
很好的资料,想下载下来学习!
回复

使用道具 举报

板凳
ID:653521 发表于 2020-5-9 22:26 | 只看该作者
谢谢楼主分享!
回复

使用道具 举报

地板
ID:748235 发表于 2020-5-10 12:17 | 只看该作者
谢谢楼主分享
回复

使用道具 举报

5#
ID:653521 发表于 2021-3-16 21:46 | 只看该作者
学习学习!顶!
回复

使用道具 举报

6#
ID:491589 发表于 2021-4-2 23:56 | 只看该作者
楼主能用如此贴切的例子,让FPGA降低了认识的难度。
回复

使用道具 举报

7#
ID:691028 发表于 2021-4-9 09:13 | 只看该作者
谢谢楼主的无私共享!学习了
回复

使用道具 举报

8#
ID:1045548 发表于 2022-9-23 11:04 | 只看该作者
楼主是大神啊!跟着大神直接起飞,6666
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表