FPGA进阶开发与实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 可编程逻辑常用设计思想和技巧

1.2.1 乒乓操作

乒乓操作实际上是利用两个数据缓存模块来进行流水操作,如图1-2所示。两个数据缓存模块轮流缓存数据,并轮流输出数据给后端处理,可以将它们看成一个连续处理数据的黑盒。实现乒乓操作的前提是输入数据的吞吐率小于输出数据的吞吐率,否则会造成数据覆盖。如果输入数据速率大于输出数据速率,但是输入数据有停顿,造成输入数据的吞吐率小于输出数据的吞吐率,就可以实现高速数据输入、低速数据输出的效果。另外,串行高速输入和并行低速输出的乒乓操作是在输入数据吞吐率小于或等于输出数据吞吐率的前提下进行的。

图1-2 乒乓操作

1.2.2 串并转换

串并转换就是把串行输入的数据并行输出,它有效地利用了FPGA可编程逻辑高并行性的特点。

1.2.3 流水操作

流水操作的实质是时序电路处理,如图1-3所示。只有把数据处理的各阶段精确分配到时序的每一个时钟周期,才能保证全流水处理输入数据。这种设计方式通常会和乒乓操作及串并转换结合起来,以保证各级流水处理的输入数据被准备好,并及时输出数据给下一级流水处理。

图1-3 流水操作

1.2.4 异步时钟域的数据同步

设计中经常会碰到不同时钟域之间的数据传输,处理好异步时钟域的数据同步问题非常重要。

1.两种典型异步时钟域

(1)两个时钟域的时钟同频不同相。

(2)两个时钟域的时钟既不同频也不同相。

2.异步时钟域数据传输方式

对于异步时钟域的数据传输(图1-4),核心思想是建立一个缓存空间,使输入数据的吞吐率小于或等于输出数据的吞吐率。这个缓存空间要支持两种时钟的读写。FPGA中的异步FIFO缓存是基于读写信号进行数据读写的,可以把输入数据用写信号异步写入FIFO缓存,输出一个半满或者满信号给读出端,读出端用读信号读出数据。

图1-4 异步时钟域的数据传输

另外,如果采集时钟频率大于输入数据时钟频率,即倍频采样,则可能出现亚稳态问题。

这个问题不能百分之百地避免,因为如果刚好采样到异步信号的跳变状态,后级寄存器输出的就是一个不确定的亚稳态信号。这时的解决方法是采用多级寄存器连续采样,这样可以保证最后的输出不是亚稳态信号,但不能完全保证后级输出是前级输入的正确值。要实现完全无错误传输,还要增加系统级纠错机制,如图1-5所示。

图1-5 加入纠错机制的异步时钟域的数据传输

1.2.5 英特尔推荐的Coding Style

采用英特尔推荐的Coding Style的好处在于代码的可移植性和可综合性。因为目前设计和器件都是超大规模的,所以依靠自动仿真、手动仿真或者设计工具的优化来使整个设计满足时序要求是很难的。在这种情况下,采用好的Coding Style能获得事半功倍的效果。

1.Coding Style的含义

Coding Style是指编程风格,好的Coding Style不依赖于具体的EDA工具和器件。也就是说,用任何一种EDA工具或者将代码移植到任何厂家的器件中,实现的功能都差不多。

2.模块层次化编程

模块层次化编程如图1-6所示。首先设计一个顶层模块,顶层模块不做具体的逻辑运算,只实现外部I/O和内部各模块之间的连接;顶层模块下面是第一层模块,它们可以相互通信;第一层模块下面的子模块只能与本层子模块和对应的第一层模块通信,不能与其他模块下面的子模块通信。这种结构便于以后的维护及测试阶段信号的搜索。

3.组合逻辑编程

组合逻辑设计最好采用assign直接赋值语句,系统会自动生成组合逻辑电路(图1-7)。如果采用always@()加敏感信号高低电平有效的赋值语句,当语句中的条件判断不完全时,非常容易生成锁存器,而在组合逻辑电路中生成锁存器,不利于静态时序分析。

图1-6 模块层次化编程

图1-7 组合逻辑电路

另外,要避免用组合逻辑链来产生延时信号。图1-8中显示了两种不同的逻辑门。因为每款FPGA器件的逻辑门的传输时间不固定,所以产生的信号宽度不一样,这不利于程序移植。

图1-8 两种不同的逻辑门

4.时钟设计编程

时钟设计的基本原则是外部主时钟通过时钟专用引脚连接到内部PLL,再由PLL产生各种时钟去驱动全局时钟网络,如图1-9所示。如果PLL资源有限,需要的时钟又太多,可以考虑用计数器生成时钟。但是,尽量不要用组合逻辑的延时来产生时钟,因为这样会产生毛刺。

图1-9 时钟设计

另外,在FPGA内部不要使用门控时钟和时钟切换等操作,这样也会产生毛刺。如果有严格的功耗要求,建议选择低功耗的器件。

5.全局复位编程

全局同步或异步复位信号一般由全局时钟网络驱动。

6.状态机编程

状态机可以将复杂的时序控制和状态跳转简单图形化。状态机编程应注意以下几点。

1)状态机的编码方式

状态机的编码方式有binary、gray-code和one-hot,编码方式直接决定译码的复杂度和速度。binary和gray-code使用的码位少,占用的寄存器资源少,但是译码的复杂度高;one-hot占用的寄存器资源多,但是译码的复杂度低,速度快。一般来说,小型状态机使用binary和gray-code编码,大型状态机使用one-hot编码。

2)编程方式

状态机编程有三种方式:第一种是把所有的状态跳转,以及跳转条件判断和状态机输入/输出写在一个always块中;第二种是将状态跳转写在一个always块中,将跳转条件判断和状态机输入/输出写在另一个always块中;第三种是把状态跳转、跳转条件判断、状态机输入/输出分别写在一个always块中,示例代码如下。

为了便于以后维护,可采用第二种或者第三种编程方式。如果要避免产生锁存器,或者需要同时确定当前状态、当前状态前后状态、状态跳转判断条件及其对输入/输出的影响,则建议采用第一种编程方式,这样更容易掌握各种状态的具体情况,而且目前的开发软件能自动识别状态机的代码,综合出正确的逻辑。

3)状态机的初始状态和默认跳转状态

每个状态机要有一个初始状态,通常采用异步复位信号;每个状态机还要有一个默认跳转状态。

4)状态机的输出默认状态

状态机必须有一个输出默认状态,以防生成锁存器。

7.三态信号编程

三态信号一般用于将顶层模块连接到PAD上面的I/O,这种I/O通常通过软件用可编程I/O模块来实现。因此,建议在顶层模块中实现三态信号。三态信号编程示例如下。