1---不详细的讲一下Xilinx的BMG:单端口和双端口RAM的区别

本文详细介绍了Xilinx Block Memory Generator (BMG)中三种BRAM类型:单端口RAM、伪双端口RAM及双端口RAM的区别。通过仿真结果展示了不同类型BRAM的工作原理及其接口特点。

Note: 以下内容可能会有错误的地方,仅供参考,详细请查阅Xilinx官方产品指南《pg058-blk-mem-gen》

BMG即Block Memory Generator,是Xilinx配置BRAM的IP核,可将BRAM配置成如图所示的5种类型

-
RAM分三种,单端口RAM、伪双端口RAM(Simple Dual)和双端口RAM(True Dual),它们之间的区别是什么呢?先看看它们的接口。

这里写图片描述

可以看到它们的接口区别

RAM类型官方解释数据读写接口数量地址接口数量
单端口允许通过一个端口对存储进行读写访问11
伪双端口提供了两个端口A和B,通过端口A进行写访问,通过端口B进行读访问。12
双端口提供两个端口A和B,这两个端口都可以对存储进行读写访问22

——
伪双端口RAM很好理解,相当于读写分开,addra、dina和wea完成写,addrb和doutb完成读,你读你的,我写我的,互不干扰。
单端口RAM的通过一个端口对存储进行读写访问是什么意思呢?通过下图的仿真可以看到,数据读端口douta一直都有输出对应地址的数据,当wea拉高时,会将数据写入对应的地址,因为该仿真选择的Operating Mode是Write First,所以wea拉高时douta输出的数据与此时写入的数据是一致的。所以单端口RAM的读写不能同时进行,在读的时候需要重新生成对应的读地址给addra,并且disable掉wea。

这里写图片描述

输出douta对应地址addra的延时为2个时钟周期,这是因为默认勾选了Output Register选项的原因,RAM在输出前打了一拍,如果去掉这个选项,延时则变为1个时钟周期。

这里写图片描述

-
双端口RAM有两个端口独立,那它们是操作不同的Memory还是操作同一个Memory呢?如果是同一个Memory不会冲突吗?先看第一个仿真,仿真条件如下:

  • 写使能web与wea相同
  • 地址addra和addrb不同
  • 输入数据dina和dinb相同

这里写图片描述

从图中可以看到addra和addrb都同时在访问Memory,因为地址不同,所以好像没有什么冲突,那如果他们同时操作一个地址会怎么样呢?

  • 写使能web与wea相同
  • 地址addra和addrb相同
  • 输入数据dina和dinb不同

这里写图片描述

从曲线看端口A和B好像是操作的不同的Memory,而注意看直线的话,会发现在wea拉高时,端口A向地址1写的是21,端口B向地址1写的是26,而wea拉低时,端口A输出的却是26,这里就可以确认端口A和B访问的是同一块Memory了。

所以通过以上仿真我们得到的结论是双端口其实是两组地址对同一块Memory进行读写

仿真里看到当两个端口同时写一个地址的时候,写入该地址的是端口B的数据,但仿真毕竟不是真实的情况,实际上是该地址的结果是未知的。ug058里有提到
这里写图片描述

pg058中还有这么一段话:
The Simple Dual-Port RAM is like a true dual-port RAM where only the Write interface of the A port and the Read interface of B port are connected.
所以对于双端口RAM,我们在外部控制的时候如果只把A端口用作写,B端口用作读,那其实就和伪双端口RAM一样了。

所以结论是:
无论是单端口RAM、伪双端口RAM还是双端口RAM,他们都只有一块Memory,通过下图同样深度和宽度设置的单端口和双端口RAM的资源消耗也可以说明。并且他们都是通过寻址的方式访问这块Memory,区别对应Memory的接口数量不同,也即是所谓的端口不同。

寻址就是输入地址,输出对应地址中存储的数据,且如果写使能wea有效,则同时将与该地址同步的输入端din的数据写入该地址。同时读写的时候会有冲突,该冲突的解决方式与选择的Operating Mode有关,详情请查看Xilinx产品指南pg058。
这里写图片描述

单端口RAM只有一个地址接口addra,对应有一对读写的数据接口dina和douta;
伪双端口RAM有两个地址接口addra和addrb,但却只有一对读写的数据接口dina和doutb,所以叫他伪双端口;
双端口RAM则拥有两个地址接口,并且每个地址接口都有对应的读写数据接口,所以叫真双端口RAM,真双端口RAM支持两个端口同时对Memory进行访问,有效的提高了访问速度。同样的,对于双端口的ROM,我们可以通过两个端口同时读取ROM中的数据,所以如果两个processor是访问同样的一组数据的话,就不需要例化两个相同的ROM了。

<think> 我们正在讨论Vivado中的简单双端口RAM。根据引用[1][2],Xilinx FPGA的RAM支持多种模式,包括单端、伪双端(即简单双端口真双端。简单双端口RAM有两个端口:一个端口通常用于写入,另一个端口用于读取。这样,它可以同时进行读写操作,从而提高数据吞吐量。 引用[1]提到: > 支持任意大小的深度数据宽度(综合时会使用一个或多个RAM原语实现);单端、伪双端、真双端三种模式;最多可以使用两个写端口;可以存在多个读端口; 引用[2]则说明: > 伪双端口RAM:2个通道,分别进行读写 因此,简单双端口(伪双端口RAM具有两个独立的端口:一个只写端口一个只读端口(或者一个端口主要用于写,另一个主要用于读)。由于读写端口分开,可以同时进行读写操作,但每个端口本身是单向的。 下面我们详细讨论简单双端口RAM的使用、原理示例。 ### 1. 原理 简单双端口RAM具有以下特点: - 两个独立的端口:端口A(通常用于写)端口B(通常用于读)。 - 每个端口有独立的时钟、地址总线数据总线。 - 由于读写操作使用同的端口,因此可以同时进行(在同一个时钟上升沿,一个端口写,另一个端口读)。 ### 2. 使用方法 在Vivado中,我们可以使用两种方式实现简单双端口RAM- **使用Block Memory Generator (BMG) IP**:通过图形界面配置生成。 - **使用HDL代码编写**:通过Verilog或VHDL描述RAM行为,由Vivado综合器推断为RAM。 #### 2.1 使用BMG IP 步骤: 1. 在Vivado中打开IP Catalog。 2. 搜索“Block Memory Generator”。 3. 配置IP- Memory Type:选择“Simple Dual Port RAM”。 - 设置数据宽度深度。 - 配置端口A为写端口,端口B为读端口。 - 可以选择是否使用输出寄存器(提高性能但增加延迟)。 - 初始化RAM内容(可选)。 4. 生成IP,并在设计中实例化。 #### 2.2 使用HDL编写 以下是一个简单的Verilog示例,实现一个简单双端口RAM: ```verilog module simple_dual_port_ram #( parameter DATA_WIDTH = 8, parameter ADDR_WIDTH = 8 )( input wire clk, input wire wea, // 端口A写使能 input wire [ADDR_WIDTH-1:0] addra, // 端口A写地址 input wire [DATA_WIDTH-1:0] dina, // 端口A写数据 input wire [ADDR_WIDTH-1:0] addrb, // 端口B读地址 output reg [DATA_WIDTH-1:0] doutb // 端口B读数据 ); // 定义RAM reg [DATA_WIDTH-1:0] ram [0:(1<<ADDR_WIDTH)-1]; // 端口A:同步写 always @(posedge clk) begin if (wea) ram[addra] <= dina; end // 端口B:同步读(无读使能,一直可以读) always @(posedge clk) begin doutb <= ram[addrb]; end endmodule ``` 在这个例子中: - 写操作(端口A)在时钟上升沿发生,当写使能`wea`为高电平时,数据`dina`被写入地址`addra`。 - 读操作(端口B)在时钟上升沿发生,地址`addrb`上的数据被读出到`doutb`(无读使能,意味着每次时钟上升沿都会读取)。 注意:此代码描述的RAM是同步读的,即读数据在时钟上升沿后输出。同时,读写可以同时进行(因为使用同的地址总线)。 ### 3. 注意事项 - **读写冲突**:简单双端口RAM的两个端口使用同的地址,因此会发生读写冲突(因为地址同)。但如果两个端口同时访问同一个地址,则读端口可能会读到旧数据或新数据(取决于时序)。在Xilinx的块RAM中,如果同时同地址读写,读出的数据可以是写入前的数据(旧数据)或写入后的新数据,具体取决于配置(通常可以通过设置输出寄存器来调整时序)。在HDL代码中,上述例子在同时同地址读写时,读出的数据是写入前的旧数据(因为读操作写操作在同一个时钟边沿,写操作还未更新该地址的数据,而读操作读取的是上一个时钟周期的存储值)。 - **初始化**:在实际应用中,RAM可能需要初始化。在HDL代码中,可以使用`initial`块初始化RAM(但注意综合时可能被忽略,推荐使用IP的初始化文件功能)。 - **资源消耗**:根据数据宽度深度,Vivado可能会使用一个或多个块RAM来实现。 ### 4. 示例:使用IP 假设我们需要一个深度为1024,数据宽度为16的简单双端口RAM,使用IP配置步骤如下: 1. 打开Block Memory Generator IP。 2. 选择“Simple Dual Port RAM”。 3. 设置端口A为写端口,数据宽度16,地址宽度10(因为$2^{10}=1024$)。 4. 端口B为读端口,同样数据宽度16,地址宽度10。 5. 其他选项保持默认(如使用输出寄存器)。 6. 生成IP后,在设计中实例化。 实例化模板类似: ```verilog blk_mem_gen_0 your_instance_name ( .clka(clk), // 端口A时钟 .wea(wea), // 端口A写使能 .addra(addra), // 端口A写地址,10位 .dina(dina), // 端口A写数据,16位 .clkb(clk), // 端口B时钟(可与clka相同) .addrb(addrb), // 端口B读地址,10位 .doutb(doutb) // 端口B读数据,16位 ); ``` ### 总结 简单双端口RAM在需要同时读写数据的场景中非常有用,例如FIFO、缓存等。在Vivado中,可以通过IP或HDL代码实现。使用IP可以方便地配置各种参数,而使用HDL代码则更灵活。 §§相关问题§§ 1. 在简单双端口RAM中,如果同时读写同一个地址会有什么结果? 2. 如何对Vivado中的RAM进行初始化? 3. 简单双端口RAM双端口RAM有哪些区别? 4. 在HDL代码中描述RAM时,如何确保综合器正确推断为块RAM? </think> ### Vivado中简单双端口RAM详解 #### 1. 基本概念与原理 简单双端口RAM(Simple Dual-Port RAM)是Xilinx FPGA中常用的存储结构,属于**伪双端口RAM**类型[^1]。其心特点是: - **两个独立端口**:一个**只写端口**(Port A)一个**只读端口**(Port B)[^2] - **同步操作**:两个端口使用独立或共享的时钟信号 - **无读写冲突**:因端口功能分离,可同时进行读写操作 - **典型应用**:FIFO缓冲器、数据采集系统(实时写入+读取) #### 2. 关键特性(来自引用) - 支持任意深度/宽度(综合时自动映射到RAM原语)[^1] - 端口独立控制:时钟、使能信号、写使能分离[^1] - 支持初始化内容字节写使能[^1] - 通过Block Memory Generator(BMGIP实现[^2] #### 3. 实现方式 ##### (1) 使用BMG IP(推荐流程) ```tcl # Vivado Tcl 配置示例 create_ip -name blk_mem_gen -vendor xilinx.com -library ip -version 8.4 -module_name sdp_ram set_property -dict { CONFIG.Memory_Type {Simple_Dual_Port_RAM} CONFIG.Write_Width_A {16} CONFIG.Write_Depth_A {1024} CONFIG.Enable_B {Use_ENB_Pin} CONFIG.Register_PortB_Output_of_Memory_Primitives {true} } [get_ips sdp_ram] ``` ##### (2) HDL直接实现(Verilog示例) ```verilog module sdp_ram #( parameter DATA_W = 16, parameter ADDR_W = 10 )( input clk, // 端口A(写) input wea, input [ADDR_W-1:0] addra, input [DATA_W-1:0] dina, // 端口B(读) input enb, input [ADDR_W-1:0] addrb, output reg [DATA_W-1:0] doutb ); (* ram_style = "block" *) // 强制综合为BRAM reg [DATA_W-1:0] mem [0:(1<<ADDR_W)-1]; always @(posedge clk) begin if (wea) mem[addra] <= dina; // 同步写 if (enb) doutb <= mem[addrb]; // 同步读 end endmodule ``` #### 4. 时序特性 | 操作 | 延迟周期 | 说明 | |------------|----------|--------------------------| | 写操作(WE) | 1周期 | 数据在时钟上升沿锁存 | | 读操作(RE) | 1-2周期 | 取决于是否启用输出寄存器 | > ⚠️ **关键点**:当读/写地址相同时,读取的是**上一时钟周期**的旧数据(同步设计特性) #### 5. 应用场景 1. **实时数据处理**:ADC采集数据写入Port A,DSP从Port B读取处理 2. **跨时钟域缓冲**:写端口用CLK_A,读端口用CLK_B(需异步FIFO控制) 3. **系数存储**:存储滤波器系数(初始化后只读Port B) #### 6. 优化技巧 1. **字节写使能**:使用`CONFIG.Enable_Byte_Write_Enable`优化部分数据更新[^1] 2. **输出寄存器**:勾选`Register_PortB_Output`提升时序性能(增加1周期延迟) 3. **资源复用**:深度<1024时自动映射到LUTRAM,节省BRAM资源 ### 示例场景:图像行缓冲器 ```verilog // 存储720p图像行 (1280像素/行) sdp_ram #(.DATA_W(24), .ADDR_W(11)) line_buffer ( .clk(pixel_clk), .wea(wr_en), .addra(wr_addr), .dina(pixel_in), .enb(1'b1), // 持续使能读 .addrb(rd_addr), .doutb(pixel_out) ); ``` > 实现功能:写入当前扫描行同时读取前一行数据
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值