基于FPGA的双口RAM实现及应用怎么用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于FPGA的双口RAM实现及应用怎么用相关的知识,希望对你有一定的参考价值。

  基于FPGA的双口RAM实现及应用的使用方法:
  CPU并行工作(双单片机系统)方式得到广泛应用。为了使2个单片机能够快速有效交换
信息,充分利用系统资源,采用双口RAM实现存储器共享是目前较为流行的方法。
  大容量、高速FPGA器件具有集成度高、体积小、灵活可重配置、实验风险小等优点,在复杂数字系统中得到越来越广泛的应用。数字电路设计采用l片FPGA器件、存储设备和一些电气接口匹配电路的解决方案已成为主流选择方案。用FPGA来实现双口RAM的功能可以很好地解决并行性和速度问题,而且其灵活的可配置特性使得基于FPGA的双口RAM易于进行修改、测试及系统升级,可降低设计成本,缩短开发周期。
  双口RAM简介:
  双口RAM是在1个SRAM存储器上具有两套完全独立的数据线、地址线和读写控制线,并允许两个独立的系统同时对其进行随机性访问的存储器,即共享式多端口存储器。双口RAM最大的特点是存储数据共享。1个存储器配备两套独立的地址、数据和控制线,允许两个独立的CPU或控制器同时异步地访问存储单元。因为数据共享。则必须具有访问仲裁控制。内部仲裁逻辑控制提供以下功能:对同一地址单元访问的时序控制;存储单元数据块的访问权限分配:信令交换逻辑(例如中断信号)等。双口RAM可用于提高RAM的吞吐率,适用于实时数据缓存。
参考技术A 基本的组件一般都是用芯片的ipcore,没必要自己实现,一般FPGA内部都是都双口RAM资源的,一般应用于双向读写场合本回答被提问者采纳 参考技术B   基本的组件一般都是用芯片的ipcore,没必要自己实现,一般FPGA内部都是都双口RAM资源的,一般应用于双向读写场合。 参考技术C 用xilinx或者quartus的ip core 生成就可以了,端口什么的都可以自己定义 参考技术D 直接用IP core定义

FPGA基础设计双口RAM乒乓操作

双口RAM经常用于跨时钟域处理,且比FIFO灵活性更大。本文给出一个具体的设计实例,让大家理解双口RAM在跨时钟域处理中乒乓操作的用法。

输入数据速率20MHz,输出数据速率100Mhz,使用双口RAM完成跨时钟域处理。一次传输的数据为1024个,假设数据位宽为8bit,使用两片宽度为8、深度为1024的双口RAM完成数据传输。

使用乒乓操作提高读写效率,写RAM1时,读取RAM2中的数据;写RAM2时,读取RAM1中的数据。数据读取速率为数据写入速率的5倍,因此写数据端可以一直保持数据写入,而读数据端按写入一组数据时间的1/5进行,使用out_valid信号表示读出的数据有效。

Vivado环境下,RAM使用Block Memory Generator IP核配置,存储类型选择为“True Dual Port RAM”。RAM在读取数据时根据配置参数会有一定的延迟,设计时要注意时序对齐(否则会容易丢掉开头或结尾的一些数据),代码如下:

`timescale 1ns / 1ps

module DualRAM
(
    input clk_wr,      //写时钟速率20Mhz
    input clk_rd,      //读时钟速率100Mhz
    input rst_n,
    input [7:0] din,   
    output reg out_valid,
    output reg [7:0] dout
);

reg [9:0] addr_wr, addr_rd;
reg en_wr1, en_wr2, we_wr1, we_wr2, en_rd1, en_rd2;
wire [7:0] dout1, dout2;

dual_port_ram u1 (
  .clka(clk_wr),      //写端口
  .ena(en_wr1),    
  .wea(we_wr1),      
  .addra(addr_wr),  
  .dina(din),   
  .douta(), 
  .clkb(clk_rd),      //读端口
  .enb(en_rd1),      
  .web(1\'b0),      
  .addrb(addr_rd),  
  .dinb(8\'d0),    
  .doutb(dout1) 
);

dual_port_ram u2 (
  .clka(clk_wr),      //写端口
  .ena(en_wr2),    
  .wea(we_wr2),      
  .addra(addr_wr),  
  .dina(din),   
  .douta(), 
  .clkb(clk_rd),      //读端口
  .enb(en_rd2),      
  .web(1\'b0),      
  .addrb(addr_rd),  
  .dinb(8\'d0),    
  .doutb(dout2) 
);

//写端口乒乓操作
always @ (posedge clk_wr)     //写地址信号控制0~1023
    if (!rst_n) addr_wr <= 1023;
    else addr_wr <= addr_wr + 1\'b1;

always @ (posedge clk_wr)     //轮流写RAM1与RAM2
    if (!rst_n) begin we_wr1 <= 1\'b1; we_wr2 <= 1\'b0;
        en_wr1 <= 1\'b1; en_wr2 <= 1\'b0;  end
    else if (addr_wr == 1023) begin
        we_wr1 <= ~we_wr1; we_wr2 <= ~we_wr2;
        en_wr1 <= ~en_wr1; en_wr2 <= ~en_wr2;
    end 
 
//读端口乒乓操作
always @ (posedge clk_rd)    //读地址信号控制0~1023
    if (!rst_n) addr_rd <= 1021;  //匹配延迟
    else addr_rd <= addr_rd + 1\'b1; 
    
reg [15:0] cnt;
always @ (posedge clk_rd)    //读时钟为写时钟的5倍
    if (!rst_n) cnt <= 16\'hFFFE;  //匹配延迟
    else if (cnt == 5119) cnt <= 0;
    else cnt <= cnt + 1\'b1;
    
reg flag1, flag2;
always @ (posedge clk_rd)    //读RAM标志,RAM1或RAM2
    if (!rst_n) begin flag1 <= 1\'b1; flag2 <= 1\'b0; end
    else if (cnt == 5119) begin flag1 = ~flag1; flag2 = ~flag2; end    
    else begin flag1 <= flag1; flag2 <= flag2; end
    
always @ (posedge clk_rd)    //读RAM使能,选择cnt的前1/5时间读取
    if (!rst_n) begin en_rd1 <= 1\'b1; en_rd2 <= 1\'b0; end 
    else if (cnt < 1024) begin en_rd1 <= flag1; en_rd2 <= flag2; end
    else begin en_rd1 <= 1\'b0; en_rd2 <= 1\'b0; end

reg en_rd1_reg, en_rd2_reg;
always @ (posedge clk_rd)    //延迟一级,匹配时序
    if (!rst_n) begin en_rd1_reg <= 0; en_rd1_reg <= en_rd1_reg; end
    else begin en_rd1_reg <= en_rd1; en_rd2_reg <= en_rd2; end

always @ (posedge clk_rd)    //输出选择,RAM1或RAM2;控制输出使能信号
    if (!rst_n) begin dout <= 0; out_valid <= 0; end
    else if (en_rd1_reg) begin dout <= dout1; out_valid <= 1; end
    else if (en_rd2_reg) begin dout <= dout2; out_valid <= 1; end
    else begin dout <= 0; out_valid <= 0; end
    
endmodule

整体仿真结果如下图所示,看以看到整个乒乓操作的流程。实际上由于时序问题,仿真时经历了很多阶段,总体而言首先要仿真写时序能准确地把数据写入到RAM中,然后再仿真读时序能正确读取数据。

读时钟clk_rd是写时钟clk_wr的5倍。写端口Write Port部分,一直保持数据写入,不停在两片RAM间切换:en_wr1和we_wr1控制RAM1的写入,第一个douta为RAM1的A端口读出的数据;en_wr2和we_wr2控制RAM2的写入,第二个douta为RAM2的A端口读出的数据。

读端口Read Port部分,en_rd1和en_rd2为RAM1和RAM2的读使能信号,轮流开启。dout1和dout2为RAM1和RAM2的读取数据,同样轮流输出有效。dout即为整体设计的输出数据,在dout1和dout2之间切换选择。当dout输出有效时,out_valid信号置高。

查看前两组写入的数据,第一组为“24、81、09…2f、44、9b”写入RAM2,第二组为“62、e9、2e…59、d7、87”写入RAM1,如下图所示:



查看输出dout和有效信号valid如下图:


上图为第一组数据从RAM2中读取,读出数据为“24、81、09…2f、44、9b”,同时out_valid信号置高。


上图为第二组数据从RAM1中读取,读出数据为“62、e9、2e…59、d7、87”,同时out_valid信号置高。可以看到输出数据保持连贯性,没有漏掉数据,表明设计正确。

以上是关于基于FPGA的双口RAM实现及应用怎么用的主要内容,如果未能解决你的问题,请参考以下文章

FPGA的双口RAM,如果读比写快很多,那读的是啥,那读的那些地址都是空吗?

关于Xilinx的FPGA中双口RAM使用的若干问题

STM32读取FPGA中双口RAM里的数据,用DMA方式如何实现

FPGA基础设计双口RAM乒乓操作

iCore3 双核心板_FPGA实验十九:基于双口RAM的ARM+FPGA数据存取实验

altera和xilinx的fpga区别