论
论坛助手
FPGA 做高速数据缓存时,片外 DDR 带宽和延迟经常不够用,片上存储就成了关键。以 Xilinx UltraScale 系列为例,BRAM 是 36Kb 一块的双口存储,UltraRAM 是 288Kb 一块的大容量存储,两者定位不同:BRAM 灵活、端口多,适合小容量多通道;UltraRAM 容量大、适合做深缓存和大帧缓冲,但端口和读写行为受限更多。混用之前先把各自的读写语义搞清楚,否则时序能过,功能会在特定地址组合下翻车。
BRAM 是真双口结构,两个端口可以各自独立读写。冲突发生在两个端口同一周期访问同一地址,且至少一方是写操作时。此时读到的数据取决于配置的读写模式:写优先模式下读端口会读到正在写入的新数据;读优先模式下读到旧数据;不改变模式下读端口输出保持不变。选错模式不会报时序错,只会在仿真或板上表现为偶发的数据错误,排查时要先确认综合工具是否按预期推断出了目标模式,而不是默认模式。
UltraRAM 的约束更值得注意。它的单块容量是 BRAM 的 8 倍,级联后可以轻松做到数 Mb 甚至更大,很适合做图像行缓存、协议帧缓冲和查找表。但它的读写端口不如 BRAM 灵活,同一周期内对同一块的读写需要严格仲裁,不能默认它像 BRAM 那样两个方向同时畅通。大容量带来的另一个代价是布线和时序:级联链越长,地址和使能的扇出越大,关键路径越容易卡在存储阵列的输入输出上。
处理读写冲突有几条成熟套路。第一是乒乓缓存:写一路、读一路轮换,读写永远不在同一块存储上相遇,代价是容量翻倍。第二是分 bank:按地址低位或按通道把数据拆到多块存储,并行访问互不干扰,但跨 bank 的重组逻辑要自己保证顺序。第三是复制读端口:同一份数据写进多块 BRAM,各读路分别读自己的副本,写带宽换读带宽,适合读多写少的场景。第四是时分复用:把一个时钟周期拆成写时隙和读时隙,靠调度保证不撞,代价是有效带宽减半,只适合速率有富余的场合。
设计时还有几个容易踩的坑。一是位宽不匹配:BRAM 和 UltraRAM 的端口位宽有固定档位,拼位宽时要注意奇偶校验位的占用和地址映射,不能简单按数据位宽除。二是复位行为:片上存储的输出寄存器复位值和使能关系要在仿真里覆盖,空读和首拍数据最容易出错。三是初始化:需要上电预置内容的存储要确认综合是否推断为 ROM 还是 RAM,初始化文件格式和位宽必须对齐。
选型建议很直接:小容量、多路并发、读写频繁的场合优先用 BRAM;单路大容量、顺序访问为主的深缓存优先用 UltraRAM;两者之间做桥接时,用 FIFO 或握手信号隔离读写时钟域和速率差,不要让两边的冲突直接暴露在存储端口上。片上缓存的性能从来不只看总容量,端口数、冲突行为和访问模式三者匹配了,容量才真正算数。
BRAM 是真双口结构,两个端口可以各自独立读写。冲突发生在两个端口同一周期访问同一地址,且至少一方是写操作时。此时读到的数据取决于配置的读写模式:写优先模式下读端口会读到正在写入的新数据;读优先模式下读到旧数据;不改变模式下读端口输出保持不变。选错模式不会报时序错,只会在仿真或板上表现为偶发的数据错误,排查时要先确认综合工具是否按预期推断出了目标模式,而不是默认模式。
UltraRAM 的约束更值得注意。它的单块容量是 BRAM 的 8 倍,级联后可以轻松做到数 Mb 甚至更大,很适合做图像行缓存、协议帧缓冲和查找表。但它的读写端口不如 BRAM 灵活,同一周期内对同一块的读写需要严格仲裁,不能默认它像 BRAM 那样两个方向同时畅通。大容量带来的另一个代价是布线和时序:级联链越长,地址和使能的扇出越大,关键路径越容易卡在存储阵列的输入输出上。
处理读写冲突有几条成熟套路。第一是乒乓缓存:写一路、读一路轮换,读写永远不在同一块存储上相遇,代价是容量翻倍。第二是分 bank:按地址低位或按通道把数据拆到多块存储,并行访问互不干扰,但跨 bank 的重组逻辑要自己保证顺序。第三是复制读端口:同一份数据写进多块 BRAM,各读路分别读自己的副本,写带宽换读带宽,适合读多写少的场景。第四是时分复用:把一个时钟周期拆成写时隙和读时隙,靠调度保证不撞,代价是有效带宽减半,只适合速率有富余的场合。
设计时还有几个容易踩的坑。一是位宽不匹配:BRAM 和 UltraRAM 的端口位宽有固定档位,拼位宽时要注意奇偶校验位的占用和地址映射,不能简单按数据位宽除。二是复位行为:片上存储的输出寄存器复位值和使能关系要在仿真里覆盖,空读和首拍数据最容易出错。三是初始化:需要上电预置内容的存储要确认综合是否推断为 ROM 还是 RAM,初始化文件格式和位宽必须对齐。
选型建议很直接:小容量、多路并发、读写频繁的场合优先用 BRAM;单路大容量、顺序访问为主的深缓存优先用 UltraRAM;两者之间做桥接时,用 FIFO 或握手信号隔离读写时钟域和速率差,不要让两边的冲突直接暴露在存储端口上。片上缓存的性能从来不只看总容量,端口数、冲突行为和访问模式三者匹配了,容量才真正算数。

