BRAM 学习
知乎:
https://zhuanlan.zhihu.com/p/51600261
1、BRAM
RAM -> Ramdom Access Memory ,随机存取存储器。何为随机存取。举个不准确的例子:和上篇文章中的 FIFO 进行对比。对于 FIFO 来说,只有读写两个操作,只能顺序读写。但对于 RAM 来说,同样的读写操作,用户可以在读写时指定读写的地址,实现对整个存储器的乱序(随机)读写访问。
BRAM -> Block RAM,花名:块 RAM。
FPGA 中有两种 RAM 资源,另一种 RAM 资源为 Distributed RAM,Distributed RAM 经过综合工具综合,通过多级 LUT 查找表资源级联实现,那么一个 Distributed RAM 可能(综合工具实际应该不会)是由天南地北,相隔很远的 LUT 级联实现的,故名分布式。分布式 RAM 可以利用丰富灵活的 LUT 资源,可以根据使用情况灵活配置,适合对 RAM 延迟要求不高的场合,毕竟并不是”真正”的 RAM。(其实物理上也是真正的 RAM,但不是专用的)
那么 Block RAM 相比分布式 RAM,就是一位专业选手了,BRAM 是 FPGA 厂商在逻辑资源之外,给 FPGA 加入的专用 RAM 块资源。相比分布式 RAM,RAM 块内部以及与逻辑资源之间经过特意的布局布线,使 BRAM 具有很高的运行速度,确定的低延迟周期,但有限的资源数量。
vivado IP核
在 Vivado 中,使用 BRAM Memory Generator 可视化工具生成 BRAM ip 核。
通过在 Ip catlog 中搜索 BRAM,就可以打开 Generator
块/分布式 RAM 有独立的生成工具。可以从 AXI4 一栏了解到该 IP 对 AXI4 协议的支持情况。支持 AXI4,AXI4-Lite,AXI-Stream 或者不支持。
Native 接口
- Native 接口分为三类:
时钟,复位信号
读写地址与数据
写使能与 RAM 使能
复位信号是可选的。
读写地址的宽度可以基于 RAM 的深度,log2(深度),也可以固定为 32 位。
RAM 写使能有效时,可以写入数据。在使用 always enable 模式下,读取在每个时钟上升沿有效,不需要额外的使能信号。不然的话 RAM 在读写时都需要 ena 使能信号有效。
Memory 类型
总体上 Memory 按照类型可以分为 RAM 和 ROM,ROM 预置了数据,在使用中只能被读取,不能写入,ROM 实现的物理结构与 RAM 类似,相当于一个只能读取的 ”RAM”。一般用于存放一些固定的参数,比如 FIR 滤波器的参数等等,在使用过程中只需要读取,不需要也不能修改。
按照端口的数量有单端口以及双端口之分,双端口来自于同时对 RAM 进行读写的需求。一边将等待处理的数据从端口 A 输入 RAM,另一端口 B 读取数据进行处理,可以实现高效的数据流式处理,尤其适用于图像的行缓存处理。双端口 RAM 相较于 FIFO ,有可以映射地址以及多次重复利用数据的优势。在新的数据写入之前,可以多次从一指定位置读取旧数据。双端口 RAM 又可以分为 Simple/Ture 双端口
读写冲突避免
对于一个 RAM 的读写时序来说,写入时,写使能有效,写入 din 端口上的数据到 addr 端口地址中。读取时,直接从 dout 端口上获得输出 addr 地址上的数据。那么当写使能有效时,用户同时进行读取操作,读取到新数据还是旧数据。这取决于 RAM 的工作模式。
工作模式分为三类:写优先,读优先以及 no change。
写优先,在读写时钟异步的情况下,推荐使用。该模式会保证写操作优先发生。
读优先,该模式以消耗更多 BRAM 资源的前提下,保证每次读操作读取到的都是先前的数据。输入数据会首先被缓存,与此同时在输出总线上输出先前值,一个周期的延迟之后,再输出输入数据。
写入期间数据不变,该模式很“佛性”,在写入期间数据不变,对读写冲突不闻不问,但优点在于节约 BRAM 功耗。
另外重要的一点是配置 RAM 的输出寄存器。共有两个选项 Primitive / Core Output Register。两个选项可以各自选择,都是为输出端添加一级寄存器,不同在于前者在 Port 内部添加寄存器,而后者是在 Port 外部添加寄存器。自然而然,每一级的寄存器都会增加一个周期的读延迟,从初始的 1 周期读延迟,最高可以增加到 3 周期读延迟。
添加寄存器虽然会增加延迟周期数,因为输出信号经过了打拍,但可以减少时钟到数据时间(即从时钟上升沿开始,直到数据出现在输出端口上的时间),改善时序。
开始仿真
对于单端口,只有一个地址addra,当wea=1时,用于写数据地址,当wea==0时用于读地址。
首先,我们观察 RAM 的读延迟。
将使能端 ena 置高,通过置高写使能信号 wea 一个周期,在时钟上升沿完成一次写入操作。可以观察到,地址总线写入 0x1,在时钟上升沿到来之前准备好了使能信号和地址信号。时钟沿上升沿到来后,完成一次写入操作。
在完成了对地址 0x01 的写入后,就可以通过读取地址 0x01 来观察 BRAM 的读延迟了。
这张仿真图中,读取地址为 0x01,可以观察到在时钟上升沿到来后,经过了 100ps 的延迟之后,0x01 地址上的数据 0xbbbb 出现在 dout 上。
这 100ps 的延迟意味,如果用户逻辑在将地址准备好后之后的第一个上升沿读取数据,那么读取到的数据就是错误的,必须等待下一个时钟上升沿才能够进行读取。从下图手册中的时序说明也可以看到这个延迟。
ena 使能端为低时,会失能 RAM 的读写操作。
ena 信号置低之前,写操作完成,但读操作被失能。
双端口RAM
在单端口 RAM 中,通常的应用可能是,先进行一系列写入,再进行一系列读取,循环反复。但也可以对一个地址,写入,读取,地址递增,再循环反复。这样的操作类似 FIFO,意义不大。
单端口 BRAM 的应用主要用于缓存,比如需要缓存一些数据,首先将所有数据放入 RAM 中,之后根据需要从不同的地址中取出数据进行运算或者处理。单端口适合读取和写入分时进行的应用。在这类应用中,不会交错进行读取写入。
连续的写入,读取操作可以使用双端口的 RAM 实现,双端口 RAM 有各种独立的地址通道和数据通道,可选各自独立的时钟。
双端口 RAM 的应用很广泛,这里举一个图像处理中的例子。
在图像处理中,图像卷积是一项基本的操作。卷积操作中,需要在将新数据缓存到 RAM 中的同时,从 RAM 中取出旧数据进行卷积运算。这时候读写逻辑控制是分开,双端口提供两套读写控制接口,适合这类读写逻辑相独立的场合。但需要注意的是读写冲突问题,在图像卷积操作中,通过将写地址固定为读地址- 0x2,解决冲突问题。
双端口工作模式
这里通过一个简单的例子,比较了 no change ,read first 以及 write first工作模式。
首先是 no change 模式,在写入期间,输出保持不变,所谓 no change。输出端口上原本为地址 0 的数据:0xaaaa,当地址 1 和地址 2 开始写入时,输出端口保持不变,在写入结束时刻,本来经过 1 /2个周期的延迟,应该输出地址 1 /2上的值:0x1111/0x2222,但是都没有,输出端口保持写入发生之前的地址 0 的数据:0xaaaa。
凡是写入期间,输出保持不变。
只在写入结束后,经过 1 个周期,输出地址 3 上的数据:0x3333 。可见,该模式的特点就是在写入时,保持输出端口不变,但会在读写冲突期间,无法读到地址 1 和地址 2 上的正确数据,读到的都是错误的数据 0xaaaa,地址 0 上的数据。
接下来是 write first 写优先模式,可以看到该模式下,写数据和将数据输出到输出总线上是同时进行的。在向地址 1 和地址 2 写入数据的同时,在对应时刻,经过一个周期延迟后,仍然正确地输出了地址 1 和地址 2 中,但注意,输出的是刚写入的新值。
最后是 read first 读优先模式,从时序上来看,读优先模式和写优先模式类似,同样接收输入的同时,按照时序将数据输出,但不同在于,写优先模式输出的是写入的新值,而读优先模式输出的是在写操作发生之前的旧值,这种不同,实际上是通过将旧值缓存实现的。下图中 0xbbbb,0xcccc 分别为地址 0x1,0x2 中原有的旧值,在向地址 0x1,0x2 写入新值,它们在dout 上被输出。