当前位置: 首页 > news >正文

FPGA设计中的数据存储

文章目录

  • FPGA设计中的数据存储
    • 为什么需要数据存储
    • FPGA芯片内部的载体
      • 触发器
      • 查找表
      • 块存储
    • FPGA芯片外部的资源
    • RAM
      • 应用场合
    • ROM
      • 特征简介
      • 实现载体
      • 应用场合
    • FIFO
      • 特征简介
      • FIFO使用小技巧之冗余法
        • FIFO 写接口缓存
        • FIFO 读接口缓存
          • “冗余法”
        • 总结
    • 根据数据流的稳定性与存储操作的容错性,决定采用RAM模式还是FIFO模式
    • STACK
      • 特征简介
      • 实现载体
      • 应用场合
      • Summary

FPGA设计中的数据存储

为什么需要数据存储

数据存储功能虽然表面上看起来并没有什么高深之处,但实际操作起来难度还是非常大的。随着FPGA芯片的集成度越来越高、FPGA设计的复杂度越来越大、业界对FPGA处理速度的期望越来越高,对于FPGA开发者使用数据存储的能力要求也越来越高。因此,在这样一个大形势下,数据存储的要求早已经不是仅仅能够将数据存下来、读出去那么简单,而是要求数据存储系统的吞吐量更大、反应速度更快、正确性更高、消耗的资源更少等等。因此,为了让数据存储不成为整个FPGA设计的短板,必须要重视对数据存储的掌控与使用。

FPGA芯片内部的载体

触发器

触发器载体的特点是:

  • 一个Flip-flop的记忆力仅为1 bit。
  • 由于规模的不同,一片FPGA芯片中的Flip-flop数量也从几千、几万甚至到几百万不等。

查找表

查找表,即FPGA逻辑资源块中的LUT,它一般用来实现组合逻辑,其实它也是具有记忆功能的。

  • 当用来实现组合逻辑时,LUT将会在程序上电的最开始,记住组合逻辑的真值表关系,从而在后续的FPGA工作中能够给出正确的功能响应。
  • FPGA的配置是基于SRAM的,即上电时的配置会在掉电后全部丢掉,而LUT中的内容是可配置的,因此每个LUT其实也就相当于一个小SRAM。
  • LUT具有记忆功能,并且它可以在每次上电的时候被配置成为不同的内容,因此也应该可以在上电期间被重新配置并保持。由此可见,LUT的记忆性并不是单次的,所以在FPGA中LUT也是数据存储的一大利器,只不过当我们在FPGA中实现组合逻辑时,可能并没有意识到自己也是在使用数据存储功能罢了。

LUT载体的特点是:

  • 若一个LUT的输入端口数为η,那么其记忆力应该为2^n bit。
  • FPGA芯片中的LUT通常为3~6输入的。
  • 一般来说,一片FPGA芯片中的LUT数量和Flip-flop数量是相当的,因此LUT也被称为分布式存储。
  • 虽然绝大多数的LUT都是可以用来作为数据存储的,但并不是所有LUT都具有被当做小SRAM来使用的特征。
  • 还有一些更高级的LUT,它们甚至还可以实现移位寄存器的功能。

块存储

块存储,即FPGA中的BLOCK RAM资源,简称BRAM,它实际上就是在FPGA芯片中嵌入的一些小型的存储器,自然也就是实现数据存储功能的主力军之一。这些BRAM往往也都比较灵活,每一片BRAM,都可以被配置为多种位宽和深度的组合,例如一个4Kb容量的BRAM,可以配置成为位宽为1 bit、深度为4K,也可以配置成为位宽为4 bit、深度为1 k,等等。

BRAM载体的特点是:

  • 一块BRAM的记忆力通常在1 Kb、甚至几十Kb以上。
  • 由于规模的不同,一片FPGA芯片中的BRAM数量也从十几、几十甚至到几百、几千不等。

FPGA芯片外部的资源

除了FPGA芯片内部的载体之外,还有很多成熟的、专门的存储芯片可供选择,包括但不限于:

  • SRAM
  • DRAM
  • SDRAM
  • OTPROM
  • EPROM
  • EEPROM
  • FLASH
  • FIFO
  • 硬盘

RAM

RAM,英文全称:Random Access Memory,翻译成中文为随机访问存储器,即我们可以在任意时刻向任意一个RAM地址写入数据或者从任意一个RAM地址读取数据。在RAM浩瀚的存储空间中,一般同一时刻最多只能访问RAM中的一个存储单元,这点与寄存器阵列有着本质的不同。

  • FPGA中的BRAM资源就是一个个独立的RAM,因此它们当然是实现RAM的首选载体。
  • 如果能够将多个LUT联合起来,其记忆力也是很客观的,因此LUT也是实现RAM的载体之一。
  • 寄存器阵列的功能是大于RAM功能的,因为同一时刻我们可以访问到寄存器阵列中的所有存储单元,所以它也可以用来作为RAM的载体,不过由于它提供的功能远远大于RAM的需求,其对触发器资源的消耗是非常大的,因此通常不建议用寄存器阵列来实现RAM。

应用场合

当需要一些大容量的数据缓存时,RAM通常是首选。并且RAM是所有涉及到大量数据存储的形式的本源,后续的所有存储形式都是在RAM的基础上发展起来的,并且,由于RAM的随机访问特性,我们可以基于此开发出适合自己的、自定义的、特殊的数据存取方式。

  • 值得注意的是,RAM的功能不仅仅限于数据的存储,因为它实际上就是一个大的查找表,可以用来实现任意的、变化无穷的逻辑功能。

ROM

特征简介

ROM,英文全称:Read-Only Memory,翻译成中文为只读存储器。它是一种预先设定好存储内容,然后只允许进行读操作的存储结构。

实现载体

从ROM的特征介绍可以看出,ROM其实就是RAM功能的一个子集,因此,触发器、查找表、块存储也都可以是ROM的实现载体。

应用场合

在FPGA设计的某些算法中,需要用到了篇幅较大、且规律不明显的常数表时,ROM是首选方案。

FIFO

特征简介

FIFO,英文全称:First In First Out,翻译成中文为先进先出队列。受到先进先出性质的约束,FIFO的应用范围远没有RAM广泛,但是在FPGA设计中,FIFO的人气指数却要远远高于RAM,这其中最主要的原因就在于绝大部分的数据传递都是遵循先进先出特性的,而且直接使用成熟的FIFO模块远比使用RAM再配合编写读、写控制逻辑要来得简便得多。

FIFO使用小技巧之冗余法

但是,当设计的工作时钟频率越来越高时,或者你的BOSS对设计的性能要求越来越苛刻时,你会发现“FIFO的数据消失性”是很令人头疼的问题。例如,以前你的设计满负荷1秒钟只能处理10幅图片,现在要达到满负荷1秒钟处理20幅图片,那么原来工作在50 MHz时钟频率下的设计,现在需要能够在100 MHz下也能正常工作。速度向来是FPGA设计最大的杀手,因为它直接关系到FPGA时序逻辑的一个至关重要的时序指标——建立时间,所以对于FPGA来说,同样一个功能,在50 MHz下和100 MHz下实现起来难度是截然不同的。

这里我们不去展开来讨论,只把目光集中到如何连续地从FIFO中读取一个不定长的数据包的问题上去。

FIFO 写接口缓存
always @(posedge clk)
beginwr_en <= wrEn;din   <= datain1;
end
FIFO 读接口缓存
always @(posedge clk)
beginrd_en <= rdEn;dout  <= dataOut;
endmyfifo fifo (.clk(clk),.din(din),.rd_en(rd_en),.srst(srst),.wr_en(wr_en),.dout(dataOut)
);

第二段代码由于对FIFO的接口增加了一级缓存,有效地截断了前、后级组合逻辑和布局、布线的延迟时间与FIFO接口时序之间的相互影响,因此最高工作时钟的频率肯定会比第一段不加缓存的代码高出不少。但是与此同时,这种简单而有效的方法带来一个重大隐患——注意观察一下第二段代码的FIFO读接口缓存部分,与写接口不同的是,读接口的使能rd_en与数据dataOut相对于FIFO是反向的,即对于FIFO来说,rd_en是输入、dataOut是输出,给它们同时加上了缓存,那就意味着后级模块在时刻0(令一个时钟周期为一个时刻)给出一个有效的使能rdEn,在时刻1便能传递给FIFO,在时刻2时FIFO才能送出这次请求所需要的数据dataOut,在时刻3数据才会传递给寄存器clout。这也就是说每当发出一次读请求的时候,要经历3个时钟周期后才能得到想要的数据。这滞后的3个周期就是这种简单又有效的方法的一个重大隐患,接下来我们会分析由这个隐患所引起的所谓的“FIFO的数据消失性”给设计带来的难题。

如果FIFO内部存储的数据包是定长的,即每次只需要读取固定长度的数据出来,那么每次读FIFO只需要连续给出N个有效使能后然后滞后第一个使能3个时钟周期后收获数据即可。但是如果需要读出来的数据是不定长的该怎么办呢?

一般,不定长的数据包有两种:

  1. 数据包的包头固定位置会存有数据包的长度;
  2. 数据包会以某种固定字节模式结尾,例如0x11 22

当连续读取FIFO时,这两种数据包结构都会产生致命问题。

  • 对于第一种数据包,如果包长度信息位于第3个字节,那么当接收到第三个字节时,如果发现当前包长度是5以内,那么就会由于来不及撤销读使能而导致“FIFO的数据消失性”发生。
  • 对于第二种数据包结构,由于结尾字节模式何时到来无法预期,必然会导致“FIFO的数据消失性”发生。

一旦“FIFO的数据消失性”发生,那么在下次读取FIFO中的数据时,得到的必然是一个不完整的数据包,如此往复将导致每次读取都得到的是错误的数据包结构,从而使得设计失败。

也许有人会说,那就不要连续读取好了,每次发送一个有效的读使能,3个时钟周期后根据收到的数据判断是否需要发送下一个读使能,这样不就可以避免“FIFO的数据消失性”发生了吗?没错,这种方法的确可以避免隐患的发生,但是,如果这样做就证明我们没有搞懂提高工作时钟频率的意义——提高工作时钟频率最大的好处就是提高了设计的性能,对于FIFO来说,就是要提高吞吐量。如果在50 MHz频率下每个时钟周期可以从FIFO中读取一个数据,而在100 MHz下每3个时钟周期才可以从FIFO中读取一个数据,那么把时钟频率从50 MHz提高到100 MHz的意义何在?因此,保证FIFO中的数据能够连续地被读出是提高FIFO吞吐量的关键。

数据包的长度是未知的,又必须连续读取,又不能让“FIFO的数据消失性”影响了数据包的结构,该如何是好呢?

你可能尝试从第二段代码的dataOut开始着手,但是dataOut是受着FIFO输出延迟影响的信号,对它进行判断很可能会降低系统的工作频率。经过一番尝试之后,你甚至可能想干脆用RAM来代替FIFO算了,因为RAM不存在“FIFO的数据消失性”,不过这就意味着你要对自己的设计动手术了。先别急,我有一个方法,在大多数情况下都可以解决这个问题,简称为“冗余法”。

“冗余法”

“冗余法”,顾名思义,就是通过一些“多余的”东西来解决问题,这些“多余的”东西看似多余,如果用得恰到好处,就一点也不多余。下面,就以固定字节模式结尾的数据包具体给出一种“冗余法”的应用例子。

假设原数据包的结构如下:

0x????, 0x????,…,0x1122;

如果在数据包的末尾追加3个无效的数据,(最好与固定结尾字节模式不同,这样有利于逻辑方面的处理),例如0x3344、0x3344、0x3344,那么数据包变成如下结构:

0x????, 0x????,…,0x1122, 0x3344, 0x3344, 0x3344;

结构修改后的数据包,采用连续读取的方法,当发现dout等于0x1122时关掉FIFO读使能rdEn,刚好将3个无效数据0x3344从FIFO中剔除出来,等到下一次读取数据包时,正好能够从新数据包的包头开始完整接收信息了!

在具体的应用中,“冗余法”可以用在数据包的尾部,也可以用在数据包的头部,也可以无所谓头部还是尾部,只要给两个数据包中间添加足够多的无效字节即可,一切按照具体情况以及逻辑处理的方便来定即可。

总结

利用冗余法从FIFO中高速且连续地读取一个不定长的数据包的思路和黑客利用缓冲区溢出漏洞对我们的PC进行攻击时,通过写以一大段NOP开头的破坏程序来增加破坏程序执行命中率有着异曲同工之处,只不过“冗余法”采用的是一小段。

根据数据流的稳定性与存储操作的容错性,决定采用RAM模式还是FIFO模式

通常来说,RAM的容错性和抗干扰性要好于FIFO。例如,如果我们现在缓存的是数据包,每个数据包的大小均为100个数据,可是由于一些原因,某一个数据包多了或者少了1个数据,如果使用FIFO,仍按照100个来读取,那么这一个错误将会扩散到后续所有的数据包中,造成群体错误,可是如果使用RAM,固定从地址0、100、200、…开始数据包的存入和读出,那么错误将不会被扩散。

再看一例,仍然是缓存数据包的,每个数据包长度仍为100,不过由于一些原因,该数据包的最后一个数据表明该包数据的有效性,若有效则缓存,无效则丢弃,针对这种情况下,如果用RAM,可以很简单地通过写地址回调来完成数据包的丢弃功能,而如果用FIFO,必须先用一个小FIFO缓存当前数据包,若该包需要存储,则送至后续大FIFO中,否则从小FIFO中连续读取100个数据并丢弃。因此,RAM相对于FIFO来说具有更好的容错性和抗干扰性,但是由于FIFO的操作不需要显式地控制地址,因此对于通信质量有保证,且无特殊需求的数据存储来说,FIFO要比RAM具有更好的易用性。

STACK

特征简介

STACK,即是堆栈的意思。与FIFO的先进先出特性恰恰相反,STACK遵循的是后进先出的原则。因此STACK的特征可以用汉诺塔的游戏原理来理解。如果你实在不知道汉诺塔是怎么回事,那么冰糖葫芦总见过吧,最后串进去的山楂球将会是第一个被你吃掉的。

实现载体

除了读取数据的位置相差甚远外,STACK和FIFO的其他性质都是一样的,因此它也是基于RAM的,所以触发器、查找表、块存储也都是STACK的实现载体,同时,为了实现后进先出的功能,还必须使用一些触发器和查找表来组成RAM的读写控制逻辑。

应用场合

STACK的思想多用于软件编程的子程序调用中,FPGA中存储数据时比较少碰到,但是当算法需要一个数据存储的后进先出特性时,别犹豫,非它莫属了!

Summary

通过对FPGA内部和外部的数据存储资源进行分析,可以明确不同应用场景下应选择的存储类型(RAM、ROM、FIFO、STACK等)。RAM和FIFO各有优缺点,应根据数据流的稳定性与存储操作的容错性来选择合适的存储模式。

http://www.lryc.cn/news/580443.html

相关文章:

  • json学习路线
  • C 中的 uintptr_t 类型
  • 深入解析享元模式:通过共享技术高效支持大量细粒度对象
  • ai 工程图相关论文集合
  • HarmonyOS:创建ArkTS卡片
  • HTML知识复习2
  • 汽车制造车间检测机器人与PLC无线以太网实时控制方案
  • 计算机技术的进阶之路:从基础到前沿的深度探索
  • 【网络】Linux 内核优化实战 - net.ipv4.tcp_timestamps
  • Oracle 高级 SQL 查询与函数详解:多表连接、子查询、聚合、分析函数
  • 3dmax一键烘焙很多张贴图合并成一张贴图插件支持fbx/obj/blender多材质模型合并为一张贴图
  • 光伏发电园区管理系统 - Three.js + Django 实现方案
  • SqueezeBERT:计算机视觉能为自然语言处理在高效神经网络方面带来哪些启示?
  • SQL 转 Java 实体类工具
  • 【内存】Linux 内核优化实战 - net.ipv4.tcp_max_tw_buckets
  • HarmonyOS学习2---Stage模型
  • 接口测试之apifox的使用
  • 在 Ubuntu 下配置 oh-my-posh —— 普通用户 + root 各自使用独立主题(共享可执行)
  • 常见高危端口风险分析与防护指南
  • java中,stream的filter和list的removeIf筛选速度比较
  • 【网络协议安全】任务12:二层物理和单臂路由及三层vlanif配置方法
  • Docker文件操作、数据卷、挂载
  • 猎板 PCB 微孔技术:构建 5G 通信设备高效运行的坚实底座
  • 冒泡和快速排序的区别
  • 【Note】《深入理解Linux内核》 第十八章:深入理解 ext2 与 ext3 文件系统
  • 人工智能-基础篇-18-什么是RAG(检索增强生成:知识库+向量化技术+大语言模型LLM整合的技术框架)
  • 2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--中篇
  • 【算法笔记】4.LeetCode-Hot100-数组专项
  • 多任务学习-ESMM
  • 隐马尔可夫模型(HMM):观测背后的状态解码艺术