前进1

125 阅读8分钟

1 FPGA重要设计思想

1 速度与面积互换原则

速度是指整个工程稳定运行所能达到的最高时钟频率,这不仅与FPGA内部各个寄存器的建立时间、保持时间、还与FPGA与外部接口的时序要求,还与相邻寄存器间的走线延时,逻辑延时有关。

面积是需要消耗的触发器,查找表等。

速度的提升往往以增大面积为代价,减少面积往往也会导致速度的牺牲。针对不同的问题需要合理的分析设计。

2 乒乓操作及串并转换思想

对数据流控制通过乒乓操作,通过多个数据缓冲区实现数据的无缝缓冲与处理,乒乓操作通过对输入数据选择控制和输出数据选择控制。使得经过缓冲的数据没有停顿地送到后续处理模块。

串并转换: 串并转换

串并转换也是高速数据流处理的一种方法,其原理就是通过移位寄存器将数据缓存,

我最近一次使用就是通过三个寄存器,我的目的是同时输出三行数据,而每次输入是串行的,所以我是通过三个寄存器,即在原先输出一行数据的基础上增加两个寄存器,通过移位的方式,延迟两拍,等第三个行同步信号到来时,也就是第四行数据到来时,同时输出三个寄存器上的数据,实现并行输出。

其实除了寄存器也可以通过fifo,双口ram,SDRAM等。因为一行数据量很大,其实我这里使用的是fifo来缓存数据,通过对时钟信号的控制实现数据的移位。使得三行数据存储到三个fifo中。通过时钟做同步信号,这里就是使用了三倍的面积来实现了三倍的数据吞吐量。

3 流水线思想

流水线的设计思想将设计拆分成多个步骤来实现,这里整个数据的处理是单向的,没有反馈和迭代。 前一个步骤的输出是下一个步骤的输入。

RISCV中的三级流水线。

4 逻辑复制和模块复用

逻辑复制,即类似的操作,我们通过分开来计算实现面积换取速度,比如我们要完成(a+b)*(c+d)。 通过面积换取速度。

我们可以使用模块复用通过一个加法器和两个二选一选择器来实现,

也可以通过逻辑复制使用两个加法器和一个二选一选择器。

逻辑复制使用两个加法器提高了速度。

而模块复用节省了面积。ac+bd。 只需要一个加法器。

5 模块化设计

FPGA设计中通过顶层模块的设计,顶层下面分为模块A,B,C等等子模块,各自完成子模块的设计与综合,可以使复杂的模块化为简单的多个模块。

6关于时钟的设计

时钟信号是整个设计最主要的部分之一,因为时序逻辑关系到功能是否可以很好的实现。

而时序问题存在很多地方,组合逻辑的延时,内部逻辑时钟带来的毛刺,都会去影响功能点实现。

1 内部逻辑的时钟 -- 避免组合逻辑直接输出,通过寄存器打一拍,达到同步。

由于组合逻辑的时钟不可避免存在毛刺,如果此时输入数据在变化过程中,那么将违反建立时间和保持时间的要求,影响后续电路的输出。

在需要使用内部逻辑时钟或者复位信号时候,我们在输出始终活着复位信号前,通过系统专用时钟PLL等打一排,避免组合逻辑直接输出,达到同步。

2 分频使能 与 使能时钟: 分频信号作为时钟使能,作为数据输入端的选择信号。

分频使能 FPGA中常用的PLL DLL等都可以。

而时能时钟就是我在上面提到的 让分频信号作为使能信号来使用,使能信号不作为时钟使用,而作为数据输入端的选择信号,这样避免了使用自己设计的分频信号。

2 跨时钟域信号处理

在FPGA多个时钟域信号通信时候,可能存在异步时钟域的问题,异步时钟域涉及两个时钟之间可能存在相位差,可能也没有频率关系即不同频不同相。 如果两个异步时钟域频率关系不确定,那么触发器之间的建立时间和保持时间也无法保证,如果违背建立时间或者保持时间,接收域会出现亚稳态的数据。

对于异步时钟域的通信,要实现同步。

1 对于单bit的慢时钟域到快时钟域,采用寄存器打两拍。

2 对于单bit的快时钟域到慢时钟域,采用异步握手信号或者扩宽时钟信号。

3 对于多bit的快时钟域问题,采用异步fifo,双口ram,专用握手。

3 亚稳态

信号传输都会有一定的时序要求,从而保证每个寄存器将捕获的输入信号正常输出。

为了保证可靠的操作,输入寄存器的信号必须在寄存器建立时间之前保持稳定,并且持续到保持时间结束。 寄存器的输入到输出需要一定的延时,也就是时钟到输出的时间Tco。如果数据信号的变化违反了建立时间或者保持时间的要求。 寄存器的输出就会处于亚稳态,此时寄存器的输出在高电平和低电平之间盘旋一段时间。

寄存器的输出达到一个稳定的高或者低的时间大于时钟到输出的时间。这样的话数据输出是不稳定的。

在同步系统中,输入信号总是能达到寄存器要求,亚稳态不会发生,在跨时钟信号传输中,由于数据信号可能在任何时间到达异步时钟域的目的寄存器,所以设计者无法保证建立时间和保持时间的要求。

如果数据信号的变化在建立时间之前并且保持到保持时间之后,那么数据达到稳定。

然而一个寄存器的输入数据违反了建立时间或者保持时间,就会延长从时钟变化到输出数据达到稳定状态的时间。超过了寄存器的固有时间。

解决办法: 一般采用同步寄存器链。

4 同步fifo

双口RAM:通过访问数组实现。由读时钟和写时钟,读有效信号和写有效信号控制 实现。

fifo设计: full不能写,empty不能读。关键在于full和empty的产生。

使用长度计数器,执行写操作,长度加1,执行读操作,长度减一。

1 fifo 对双口ram通过read_allow 和wirte_allow,实现数据的存储和读取。

2 而read_allow 和wirte_allow是通过full和empty,read_enable,write_enable来实现的,所以full和empty的生成很重要。空满信号又是根据Fcount计数器间接控制,Fcount的生成也很关键。

3 Fcount 的更新

空信号由 高7位为空,最低位为0或者读信号有效生成。

满信号由 高7位为1,最低位为1或者写信号有效生成。

读写控制通过对双口ram 地址加1实现。

    wire Read_allow = (Read_enable && !empty);  // 空不能读
    wire Write_allow = (Write_enable && !Full); //满不能写
    
    DUALRAM U_RAM(
    .Read_clock(Clock),
    .Write_clock(Clock),
    .Read_allow(Read_allow),
    .Write_allow(Write_allow),
    .Read_addr(Read_addr),
    .Write_addr(Write_addr),
    .Write_data(Write_data),
    .Read_data(Read_data)
    );
 //  空满信号的产生  
always@(posedge Clock or posedge Fifo_rst)
      if(Fifo_rst)
          Empty <= 1'b1;
      else
          Empty <= (!Write_enable && (Fcount[8:1] == 8'b0) && ((Fcount[0] == 1'b0) || Read_enable));
          
always@(posedge Clock or posedge Fifo_rst)
    if(Fifo_rst)
        Full <= 1'b1;
    else
        Full <= (!Read_enable && (Fcount[8:1] == 8'hFF) && ((Fcount[0] == 1'b1) || Write_enable));
// 计数器

always@(posedge Clock or posedge Fifo_rst)
     if(Fifo_rst)
        Fcount <= 1'b0;
     else if(read_enable && write_enable)
         Fcount <= Fcount;
     else if(Read_allow)
         Fcount <= Fcount - 1'b1;
     else if(Write_allow)
         Fcount <= Fcount + 1'b1;
     else
         Fcount <= Fcount;
         
// 写数据  读数据控制。 通过Read_allow和Write_allow和双口ram联系起来
always@(posedge Clock or posedge Fifo_rst)
      if(Fifo_rst)
          Read_addr <= 1'b0;
      else if(Read_allow)
          Read_addr <= Read_addr + 1'b1;
always@(posedge Clock or posedge Fifo_rst)
      if(Fifo_rst)
          Write_addr <= 1'b0;
      else if(Write_allow)
          Write_addr <= Write_addr + 1'b1;


endmodule


5 异步fifo

异步fifo核心 :

1 双口ram

2 控制地址自增

3 二进制地址转化为格雷码地址

4 格雷码地址跨时钟域

5 对同步后的格雷码进行解码 格雷码转二进制码

6 full 与empty信号的产生

扩展一位地址位 高位相等则为空 ,不相等则为满。

还有一个点就是控制地址自增的时候,需要判断。 对于写地址递增,写信号有效且没有写满。 对于读地址递增,读信号有效且没有为空。

格雷码的跨时钟域,写地址同步到读时钟域,读地址同步到写时钟域打两拍。

对同步后的格雷码进行解码。

full和empty的产生。写满为 判断写地址和写时钟域读地址。最高位不同,且地址相同为满。 写满为写指针超过读指针一个存储深度,并处于同一位置。

也就是写指针超过了同步后的读指针一圈。

读空判断,最高位相同为空。读指针和写指针一致,则读空了。也就是同步后的读指针追上了写指针

6 异步fifo中格雷码的作用

跨时钟域处理方法: 寄存器打两拍和格雷码。 格雷码只有一位不同,减少同步过程中的同步错误。降低了产生亚稳态的概率,以及逻辑冒险的概率。

格雷码的作用就是即使在亚稳态进行读写指针抽样, 也能进行正确的空满状态指示。

异步fifo的写指针和读指针分属于不同的时钟域,这样指针在进行同步的过程中容易出错。采用格雷码,相邻每位只有一位发生变化,这样在进行指针同步时候,就只会产生同步正确。即使同步出错,出错的结果也是写指针的跳变保持不变。对于读空判断,最多导致让空标志在fifo不是真正为空的时候产生,而不会出现读空的情形。

7 格雷码和二进制的互相转换

二进制转格雷码:二进制右移一位 和自己 做异或 a[1]>>1 ^ a[1] 其实就是最高位不变 其余位与前一位做异或。

格雷码转二进制:二进制码次高位 为高位二进制码和格雷码的异或。

8 FIFO的设计以及fifo最小深度

full 和empty产生。

异步fifo,格雷码跨时钟域,格雷码与二进制的转换。

fifo最小深度的计算:

背靠背问题:写入和读取不是均匀写入和读取的时候。

为了得到更安全的FIFO深度,我们需要考虑最坏情况,以防数据丢失,读写的速率应该相差最大,也就是说需要找出最大的写速率 和 最小的读速率,这样才能适配所有写入和读取的情况

写数据时钟频率fa=50MHz
读数据时钟频率fb=40MHz
在写时钟周期内,每80个周期就有40个数据写入FIFO
在读时钟周期内,每10个周期可以有6个数据读出FIFO

写入 80个数据需要的时间: 80 * 1/50MHZ = 1600ns

读速率设置为最小的40MHZ * 6/10 = 24MHZ

在1600ns内可以读出 1600 / 1/24MHZ = 38.4个数据

所以所以FIFO_DEPTH(min) = 80- 38.4 = 42。