FPGA RAM简介和使用案例

描述

在FPGA 逻辑设计中经常用到的数据存储方式有ROM、RAM和FIFO,根据不同的应用场景选择不同的存储方式。Xilinx 平台三种存储方式在使用过程中的区别如下:

1、ROM按照地址读写,使用初始化.ceo文件将地址和对应的数据内容存入,读数据的时候给地址,输出地址中存储的数据。支持反复读取,读取过程中不会使数据减少;

2、RAM按照地址读写数据,按照指定的地址写入数据,读数据的时候给地址,输出地址中存储的数据,支持反复读取,读取过程中不会使数据减少;

3、FIFO没有地址参与,先写入的数据被先读出,就是先进先出,读取数据的过程中读一个少一个,就像鸡蛋放在篮子中取出一个少一个。

01 RAM简介

RAM,random access memory,是随机存取存储器的缩写,掉电后数据丢失。 这里使用简单双端口RAM举例,即端口A写数据,端口B读数据。

RAM

端口A写入数据的过程中WEA==1'b1 && ENA==1'b1,条件同时满足的时候,DINA的数据被写入到指定的内存地址中。

RAM

端口B读出数据的时候,读使能和读地址同时有效,读出数据需要延迟一个时钟周期。

RAM

1.1、vivado中添加RAM-IP核

step1:在ip-catalog中搜索ram,找到 block memory generator

RAM

step2:在ip核配置

RAM

step3:端口A设置(写入数据位宽和深度)

RAM

step4:端口B设置(注意细节)

RAM

step5:其他设置

RAM

02 RAM使用案例

2.1、简单双端口RAM使用案例

简单双端口RAM使用的案例有1、数据缓冲-实现位宽转化;2、对应连续待处理的数据流使用乒乓RAM,实现数据流不间断的输入到处理模块。本文主要对乒乓RAM做一个详细介绍和测试应用。

RAM

2.2、乒乓RAM读写时序设计

乒乓RAM读写时序设计波形图中读写时钟使用了相同的时钟信号,当读写数据的时钟不同时,就是异步乒乓RAM。首先对RAMA写入数据,按地址写入数据结束以后读取RAMA的数据给数据处理模块,同时将外部输入的数据缓存到RAMB中,保证同一时间内既有数据缓存又有数据输出,实现的效果就是外部不间断地输入数据,经过RAM处理以后不间断地输出到下一级处理模块。

RAM

图2-2-1、乒乓RAM读写时序设计

2.3、代码实现

根据时序设计波形图2-2-1,编写逻辑代码,
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/01/08 19:19:47
// Design Name: 
// Module Name: pingpang_ram
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module pingpang_ram(
input      wire                sclk,
input      wire                async_rst_n,   
input      wire                wr_valid, 
input      wire [7:0]          data_in,
output     wire [7:0]          data_out
    );


//  信号定义 
localparam                  ADDR_MAX                  =              1024       -        1;  
// rama   
reg                        wr_en_a;
reg  [9  :  0]             wr_addr_a;           // 写地址  
reg                        rd_en_a;
reg  [9  :  0]             rd_addr_a;           // 读地址  
wire [7  :  0]             rd_data_a;      
reg                        wr_en_a_dly;
// ramb  
reg                        wr_en_b;
reg  [9  :  0]             wr_addr_b;           // 写地址  
reg                        rd_en_b;
reg  [9  :  0]             rd_addr_b;           // 读地址  
wire [7  :  0]             rd_data_b;     
//  
wire   sync_rst_n; 
reg    sync_rst_n1; 
reg    sync_rst_n2; 


assign      sync_rst_n           =              sync_rst_n2 ;
assign    data_out               =              (wr_en_a_dly   ==  1'b0 )  ?     rd_data_a    :    rd_data_b;         // 符合条件后---立即响应 


// 异步复位,同步释放,异步复位信号,同步处理
always@(posedge sclk or negedge async_rst_n)   begin 
    if(!async_rst_n)   begin 
    sync_rst_n1    <=     1'b0;                             //  复位开始的时候 wr_en 就开始有效    
    sync_rst_n2    <=     1'b0;     
    end  
    else  begin 
    sync_rst_n1    <=       1'b1;   
    sync_rst_n2    <=       sync_rst_n1;  
     end 
end  
//  wr_en   
always@(posedge sclk or negedge sync_rst_n)   begin 
    if(!sync_rst_n)   begin 
    wr_en_a   <=     1'b0;                                
    end  
    else if (wr_valid  ==   1'b1   )  begin 
    wr_en_a     <=       1'b1;     
    end    
    else if(wr_addr_a   ==   ADDR_MAX)   begin  
    wr_en_a     <=      1'b0;
    end 
    else if(rd_addr_a   ==   ADDR_MAX)   begin  
    wr_en_a     <=      1'b1;
    end   
end 


//   写地址信号  
always@(posedge sclk or negedge sync_rst_n)   begin 
     if(!sync_rst_n)   begin 
     wr_addr_a     <=      16'd0;                          
     end  
     else if(wr_addr_a   ==   ADDR_MAX) begin
     wr_addr_a     <=       16'd0;       
     end       
     else if(wr_en_a   ==  1'b1 )  begin                    
     wr_addr_a     <=    wr_addr_a      +     1'b1;
     end 
end 
//   读使能信号  
always@(posedge sclk or negedge sync_rst_n)   begin 
     if(!sync_rst_n)   begin 
      rd_en_a          <=            1'b0;                          
     end  
     else if(wr_addr_a   ==   ADDR_MAX) begin                    
      rd_en_a          <=            1'b1;    
     end 
      else if (rd_addr_a    ==     ADDR_MAX) begin                  
      rd_en_a          <=            1'b0;     
     end 
end 
always@(posedge sclk or negedge sync_rst_n)   begin 
      wr_en_a_dly          <=            wr_en_a;                          
     end      
//   读地址信号  
always@(posedge sclk  or  negedge sync_rst_n)   begin 
    if(!sync_rst_n)   begin 
      rd_addr_a          <=     10'd0;                             
    end  
    else if(rd_addr_a   ==  ADDR_MAX) begin
      rd_addr_a          <=         10'd0;  
    end      
    else if(rd_en_a    ==     1'b1)   begin                         
      rd_addr_a         <=     rd_addr_a          +        1'b1;
    end 
end 
// ---------RAMB   
always@(posedge sclk or negedge sync_rst_n)   begin 
    if(!sync_rst_n)   begin 
    wr_en_b   <=     1'b0;                                 
    end  
    else if(wr_addr_b   ==   ADDR_MAX)   begin  
    wr_en_b     <=      1'b0;
    end 
    else if(wr_addr_a   ==   ADDR_MAX)   begin                 //  写完RAMA -开始写RAMB    
    wr_en_b    <=      1'b1;
    end   
end 
//   写地址信号  
always@(posedge sclk or negedge sync_rst_n)   begin 
     if(!sync_rst_n)   begin 
     wr_addr_b     <=      16'd0;                            
     end  
     else if(wr_addr_b   ==   ADDR_MAX) begin
     wr_addr_b     <=       16'd0;       
     end       
     else if(wr_en_b   ==  1'b1 )  begin                    
     wr_addr_b     <=    wr_addr_b      +     1'b1;
     end 
end 
//   读使能信号  
always@(posedge sclk or negedge sync_rst_n)   begin 
     if(!sync_rst_n)   begin 
      rd_en_b          <=            1'b0;                          
     end  
     else if(wr_addr_b   ==   ADDR_MAX) begin                    
      rd_en_b          <=            1'b1;    
     end 
      else if (rd_addr_b    ==     ADDR_MAX) begin                  
      rd_en_b          <=            1'b0;     
     end 
end    
//   读地址信号  
always@(posedge sclk  or  negedge sync_rst_n)   begin 
    if(!sync_rst_n)   begin 
      rd_addr_b          <=     10'd0;                              
    end  
    else if(rd_addr_b   ==  ADDR_MAX) begin
      rd_addr_b          <=         10'd0;  
    end      
    else if(rd_en_b    ==     1'b1)   begin                          
      rd_addr_b         <=     rd_addr_b          +         1'b1;
    end 
end 
//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
ram_8x1024       a_instance_name (
  .clka(sclk),            // input wire clka
  .ena(wr_en_a),       // input wire ena
  .wea(wr_en_a),       // input wire [0 : 0] wea
  .addra(wr_addr_a),   // input wire [9 : 0] addra
  .dina(data_in),         // input wire [7 : 0] dina
  .clkb(sclk),            // input wire clkb
  .enb(rd_en_a),       // input wire enb
  .addrb(rd_addr_a),   // input wire [9 : 0] addrb
  .doutb(rd_data_a)       // output wire [7 : 0] doutb
);


//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
ram_8x1024       b_instance_name (
  .clka(sclk),               // input wire clka
  .ena(wr_en_b),        // input wire ena
  .wea(wr_en_b),        // input wire [0 : 0] wea
  .addra(wr_addr_b),    // input wire [9 : 0] addra
  .dina(data_in),            // input wire [7 : 0] dina
  .clkb(sclk),               // input wire clkb
  .enb(rd_en_b),        // input wire enb
  .addrb(rd_addr_b),    // input wire [9 : 0] addrb
  .doutb(rd_data_b)          // output wire [7 : 0] doutb
);
endmodule

仿真激励文件

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/01/08 20:24:11
// Design Name: 
// Module Name: tb_pingpang_ram
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module tb_pingpang_ram();
reg                       sclk;
reg                       async_rst_n;
reg                       wr_valid;
reg [7:0]                 data_in;
wire [7:0]                data_out;


initial  begin 
sclk    =        0;
forever          #5
sclk    =       ~sclk;
end  


initial  begin 
        async_rst_n          <=        0;
        wr_valid             <=        0;  
        #100  
        async_rst_n          <=        1;
        #10
        @(posedge    sclk)
        @(posedge    sclk)
        @(posedge    sclk)
        @(posedge    sclk)
        wr_valid                  <=             1;       
        #10
        wr_valid                  <=             0;
        gen_data(  );
end  
//@(posedge    wr_valid)
//gen_data(  );
//end 
task  gen_data;
integer     i;  
begin
    for(i= 0;      i   <   12288;      i =    i   +   1)  begin
        @(posedge   sclk)
           data_in     =       i[7:0];
        end  
    end 
endtask   
pingpang_ram     u_pingpang_ram(
    .sclk        ( sclk        ),
    .async_rst_n ( async_rst_n ),
    .wr_valid    ( wr_valid    ),
    .data_in     ( data_in     ),
    .data_out    ( data_out    )
);
endmodule

2.4、仿真验证结果

RAM

图2-2-1、乒乓RAM仿真结果-输出数据连续

RAM

图2-2-2、乒乓RAM仿真结果-输入-输出数据对应

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分