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 的警告给你。也可以尝试做一些仿真体会一下。
一号门 = A + B 二号门 = C + D.
三号门 = 一号门 + 二号门
四号门 = 三号门 + E
这样通过一个三层门的结构做一个五输入的加法器,来达到最好的效果。
6 输入查找表 (StratixII, CycloneII, 和其他那些比较新的器件)
在新的结构中,我们可以使用 6 输入的查找表,这样,就可以用三个数加在一起 变成:
一号门 = A + B + C
二号门 = 一号门 + D + E
Design Space Explorer: 这个是一个实在没办法的办法,但凡还有一线生路,最好不要用这个东西。他会自动的对你的设计根据一些设置,不断的尝试布局布线。比如使用不同的优化方法,不同的算法,甚至不同的种子来做编译,最后给你一个最符合约束要求的编译结果。但是这样的工作量有多大,大家可想而知了。如果你有比较多的时间,你可以试试把它扔在那里跑个几天。
不求甚解之 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
再了解一下 Jtag Debug 的模式选择,一共有五个等级的选择。和选择 CPU 一样,从简单到复杂。一般来说选择 Level2 也就够用的。
自定义指令设置。这是最有价值的设置。别忘记了,我们是在 FPGA 的世界里。所以 CPU并不像在其他地方那样的铁板一块。我们可以选择使用自定义的指令。所谓自定义指令,并不是一个软件宏或者函数。而是一块硬件。当 CPU 调用到这个指令的时候,事实上它调用的就是这个硬件模块,它被嵌入在 CPU 中。而这其实就是 NiosII 好玩的地方。
好了,现在我们要考虑的问题,就是使用。使用 CPU 的方法,当然就是软件编程。NiosII的软件其实是非常简单的。就是普通的 C,或者 C++,需要做的就是不断的对端口的地址读啊,写啊,计算数据,就好了。但也可能非常的复杂,因为你不仅需要了解软件编程,你更需要了解你使用的那些硬件,那些外设模块。NiosII 的编程很硬件的依赖性是很强的。针对比较大型的一些外设,可以写一些 HAL 程序。这是类似于驱动的一些指令。而软件只需要调用这些 API 就可以了。大部分的 NiosII 程序是不需要使用操作系统的,作为一个嵌入式系统的控制核心,更多的是一些存储式的读写,算法的计算的操作。除非你需要运行一些网络协议啊,什么的。但其实我们可以用更加解构的方式来看待一个操作系统。操作系统其实就是给我们提供了一大堆的操作指令而已。没什么更特别的作用了。所以,思考一下吧。
大家知道我们这里说玩转 FPGA 的,每次我在 FPGA 里面提到了 DSP 的题目的时候,总是会有一些人打断我说,兄弟啊,你大概走错地方了。我们这里都是硬件工程师,或者可能有些写 C 的,但是 DSP,那东西不会写哦。那么好吧,我就跑到 DSP 那里去讲,FPGA,然后就会有人问我,你们这个 FPGA 和 TI 的 DSP 比有什么区别呢?我晕。我想说的是,我这里的 DSP 是 Digital Signal Processing (数字信号处理),而不是 Digital Signal Processor (数字信号处理器)。
1. 多通道支持,在这个模块组中,接口都异常的简单,基本上就是这样三个,V, D, C. V就是 Valid, D 就是 Data, C 就是 Channel。所以要告诉他的就是,是个数据,是不是有效数据,是那个通道上的有效数据。所以,无论你是多少通道的设计,无论你怎么修改你的通道数目,模型就还是这么个模型,都是一样的。这样可以使你的模型和你的算法框图看上去几乎是一样的。
3. 系统层面的设计。这也是一个比较新鲜的东西。所有设计里面的寄存器都会被编入一个系统地址查找表,比如说 FIR 的系数,一些控制寄存器都会有不同的地址。我们可以通过一个系统接口来对这些寄存器进行操作。这样使整个设计更具有系统化概念。在编译的时候,同时非常高级的生成一个寄存器列表(网页格式),包括寄存器名字,地址,初始值。所以可以见到,通过这个高级的模块的增强,使得算法方面的实现与设计变得更加容易。也可以很容易的实现非常复杂的系统。