此篇是我在学习中做的归纳与总结,其中如果存在版权或知识错误请直接联系我,欢迎留言。
PS:本着知识共享的原则,此篇博客可以转载,但请标明出处!
引言:
存储单元在FPGA设计中几乎是不可或缺的。无论是单端口(SP)、简单双端口(SDP)或真双端口(TDP),也无论是采用BlockRAM或分布式RAM(Distributed RAM)实现,都可以采用如下几种方式:
1)、RTL代码
具有较强的可控性和可测性,但未必能获得最优的综合结果;
2)、IP Core
比较快速且灵活,能根据性能或资源需求获得期望的综合结果,但如果需要
更换Memory的实现方式或IP版本升级,就需要重新调用IP生成
3)、XPM_MEMORY
相比之下,XPM_MEMORY就很好地继承了这些方式的优点。
关键参数说明:
详情可参照Xilinx UG974文档
参数名称 | 默认值 | 说明 |
ADDR_WIDTH_A | 32 | 指定端口 A 地址端口 addra 的宽度,以位为单位。必须足够大才能从端口 A 访问整个内存,即 >= $clog2(MEMORY_SIZE / WRITE_DATA_WIDTH_A)。 |
AUTO_SLEEP_TIME | 0 | 自动睡眠的 clk[a|b] 周期数,如果功能在架构中可用 • 0 - 禁用自动睡眠功能 • 3 - 15 - 自动睡眠延迟周期数 不要更改模板中提供的值实例化 |
BYTE_WRITE_WIDTH_A | 32 | 在端口 A 上启用字节宽写入,请指定字节宽度,以位为单位 • 8 - 8 - 位字节宽写入,当 WRITE_DATA_WIDTH_A 是 8 的整数倍时合法 • 9 - 9 - 位字节宽写入,在以下情况下合法WRITE_DATA_WIDTH_A 是 9 的整数倍 或者要在端口 A 上启用字宽写入,请指定与 WRITE_DATA_WIDTH_A 相同的值。 |
CLOCKING_MODE | "common_clock" | • "common_clock" - 通用时钟;使用 clka 为端口 A 和端口 B 提供时钟 • “independent_clock” - 独立时钟;带有 clka 的时钟端口 A 和带有 clkb 的端口 B |
ECC_MODE | "no_ecc" | |
MEMORY_OPTIMIZATION | "true" | “true”以启用优化未使用的内存或内存结构中的位。 “false”以禁用优化未使用的内存或内存结构中的位 |
MEMORY _PRIMITIVE | "auto" | 指定要使用的内存原语(资源类型) • "auto" - 允许 Vivado Synthesis 选择 • "distributed" - 分布式内存 • "block" - 块内存 • "ultra" - 超 RAM 内存 |
MEMORY_SIZE | 2048 | 以位为单位指定总内存阵列大小。例如,对于 2kx32RAM,输入 65536。 • 当启用 ECC 并设置为“encode_only”时,内存大小必须是READ_DATA_WIDTH_B 的倍数 • 当启用 ECC 并设置为“decode_only”时,内存大小必须是WRITE_DATA_WIDTH_A的倍数 |
READ_DATA_WIDTH_B | 32 | 指定端口 B 读取数据输出端口 doutb 的宽度,以位为单位。 • 当启用 ECC 并设置为“encode_only”时,READ_DATA_WIDTH_B 必须是 72 位的倍数 • 当启用 ECC 并设置为“decode_only”或“both_encode_and_decode”时,READ_DATA_WIDTH_B 必须是 64 位的倍数 |
READ_LATENCY_B | 2 | 指定端口 B 读取数据管道中的寄存器级数。读取输出到端口 doutb 的数据需要此数量的 clkb 周期(当 CLOCKING_MODE 为“common_clock”时为 clka)。要定位块内存,需要 1 或更大的值 - 1 导致仅使用内存锁存器; 2 导致使用输出寄存器。要定位分布式内存,需要 0 或更大的值 - 0 表示组合输出。大于 2 的值会合成额外的触发器,这些触发器不会重新定时到内存基元中。 |
USE_EMBEDDED_CONSTRAINT | 0 | 1:启用分布式 RAM 的 clka 和 clkb 上的 doutb_reg 之间的 set_false_path 约束添加 |
USE_MEM_INIT | 1 | |
WAKEUP_TIME | "disable_sleep" | 指定“disable_sleep”禁用动态节能选项,指定“use_sleep_pin”启用动态节能选项 |
WRITE_DATA_WIDTH_A | 32 | 64 位的倍数 当启用 ECC 并设置为“decode_only”时,WRITE_DATA_WIDTH_A 必须是 72 位的倍数 |
WRITE_MODE_B | "no_change" | "no _change", "read _first", "write _first" |
使用说明:
为了方便说明,首先举例IP核的设置:
如下图,我们要使用异步双口BRAM,
写位宽 512 深度 2 ^ 8
读位宽 32
例化如下:
Wr512x256Rd32x4096BRAM u_RdBram(
.clka ( InDdrClk ) , //: IN STD_LOGIC;
.ena ( 'b1 ) , //: IN STD_LOGIC;
.wea ( InRdBufEn ) , //: IN STD_LOGIC_VECTOR(0 DOWNTO 0);
.addra ( InRdBufAddr ) , //: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
.dina ( InRdBufData ) , //: IN STD_LOGIC_VECTOR(511 DOWNTO 0);
.clkb ( InRdBramClk ) , //: IN STD_LOGIC;
.enb ( 'b1 ) , //: IN STD_LOGIC;
.addrb ( RdBramAddr ) , //: IN STD_LOGIC_VECTOR(11 DOWNTO 0);
.doutb ( OutRdBramData ) //: OUT STD_LOGIC_VECTOR(31 DOWNTO 0)
);
XPM_MEMORY参考代码:
其中:
ADDR_WIDTH_A可参照IP Core的IN STD_LOGIC_VECTOR(7 DOWNTO 0);设置为8;
ADDR_WIDTH_B可参照IP Core的IN STD_LOGIC_VECTOR(11 DOWNTO 0);设置为12;
CLOCKING_MODE 设置为异步时钟 "independent_clock"
BYTE_WRITE_WIDTH_A和WRITE_DATA_WIDTH_A设置为 DDR_MIG_DW = 512
BYTE_WRITE_WIDTH_B设置为 32
MEMORY_SIZE 设置为:DDR_MIG_DW * 2 ^ 8
xpm_memory_sdpram #(
.ADDR_WIDTH_A ( 8 ) , // DECIMAL
.ADDR_WIDTH_B ( 12 ) , // DECIMAL
.AUTO_SLEEP_TIME ( 0 ) , // DECIMAL
.BYTE_WRITE_WIDTH_A ( DDR_MIG_DW ) , // DECIMAL
.CLOCKING_MODE ( "independent_clock" ) , // String
.ECC_MODE ( "no_ecc" ) , // String
.MEMORY_INIT_FILE ( "none" ) , // String
.MEMORY_INIT_PARAM ( "0" ) , // String
.MEMORY_OPTIMIZATION ( "true" ) , // String
.MEMORY_PRIMITIVE ( "block" ) , // String
.MEMORY_SIZE ( DDR_MIG_DW * 256 ) , // DECIMAL DDR_MIG_DW * 2^8
.MESSAGE_CONTROL ( 0 ) , // DECIMAL
.READ_DATA_WIDTH_B ( 32 ) , // DECIMAL
.READ_LATENCY_B ( 2 ) , // DECIMAL
.READ_RESET_VALUE_B ( "0" ) , // String
.USE_EMBEDDED_CONSTRAINT( 0 ) , // DECIMAL
.USE_MEM_INIT ( 1 ) , // DECIMAL
.WAKEUP_TIME ( "disable_sleep" ) , // String
.WRITE_DATA_WIDTH_A ( DDR_MIG_DW ) , // DECIMAL
.WRITE_MODE_B ( "no_change" ) // String
) Wr512x256Rd32x4096(
.dbiterrb ( ) , // 1-bit output
.doutb ( OutRdBramData ) , // READ_DATA_WIDTH_B-bit output
.sbiterrb ( ) , // 1-bit output
.addra ( InRdBufAddr ) , // ADDR_WIDTH_A-bit input
.addrb ( RdBramAddr ) , // ADDR_WIDTH_B-bit input
.clka ( InDdrClk ) , // 1-bit input
.clkb ( InRdBramClk ) , // 1-bit input
.dina ( InRdBufData ) , // WRITE_DATA_WIDTH_A-bit input
.ena ( InRdBufEn ) , // 1-bit input
.enb ( 1'b1 ) , // 1-bit input
.injectdbiterra ( 1'b0 ) , // 1-bit input
.injectsbiterra ( 1'b0 ) , // 1-bit input
.regceb ( 1'b1 ) , // 1-bit input
.rstb ( InDdrRst ) , // 1-bit input
.sleep ( 1'b0 ) , // 1-bit input
.wea ( InRdBufEn ) // WRITE_DATA_WIDTH_A-bit input
);
注意:建议enb常置一,否则读数据最后一位会出错!
仿真结果如下图所示: