IP核-RAM

一、ISE

1.创建ISE工程,IP核需要在ISE工程里面进行调用。点击Tools---Core Generator...

2.在新弹出来的界面中创建一个属于IP核的工程:file---new project,并填写文件存储位置和文件名称,一般为ipcore_dir文件夹,点击保存

3.弹出的Part处填写器件的系列、型号、封装以及速度等级,Generation处设置语言为Verilog,点击OK

4.点击文件夹,找到Memories & Storage Elements---RAMs & ROMs---Block Memory Generator,(也可以直接搜索)双击打开,进行参数设置

5.设置模块名称,Next

6.类型选择,一般选Single Dual RAM,该RAM为“a口负责写,b口负责读”,而对于真双口RAM来说,a和b都是可读可写。其他选项根据需要勾选。Next

7.RAM的位宽、深度、使能选择,Next

 

 8.是否在B端添加一个寄存器(加了寄存器可以使RAM输出的数据更稳定)?本来ram的输出就慢1clk,勾选了又慢1clk,所以一般不勾选。是否需要初始化并加载初始化ceo文件,Next

 9.是否对B口添加复位键,Next

 10.总结页面,注意里面一句话:B口的读取有1clk的延迟,点击Generate即可生成RAM。大功告成!

二、Vivado

1、配置介绍

在FPGA中经常会遇到存储数据的问题,数据量过大时,可以将其存储在FPGA芯片的外设存储器上,比如sdram、ddr、ddr3等,然而访问外设存储器相对比较麻烦,因为当数据量较小时,我们可以直接使用FPGA芯片内部自带的RAM的IP核。
RAM是random access memory的简称,即随机存储器的意思,Ram可以按照所需进行随机读/写。我们可以通过调用FPGA内部的IP核生成一个ram,并通过编写Verilog HDL代码控制该ram。
1.进入工程,选择IP Catalog:
 

RAM1


2.在IP Catalog界面搜索框中输入RAM,在Memories & storage Elements选项下有两种IP,一种是DRAM(Distributed Memory Generator),一种是BRAM(Block Memory Generator): 这里的DRAM并不是动态存储器,而是分布式存储器,与BRAM区别在于,DRAM通过FPGA中的查找表拼凑而成,而BRAM是FPGA中整块双口RAM资源。这里学习BRAM的使用,双击Block Memory Generator。
 

RAM2


 

RAM3


Interface Type:支持AXI4和AXI4Lite,默认为Native。
Memory Type:IP支持生成5种类型的存储:
Single-port RAM(单口RAM),通过单组接口读写一块存储空间,接口如下:
 

单口RAM


Simple Dual-port RAM(简单双口RAM),有A和B两组接口,其中A接口用来写RAM,B接口用来读RAM,接口如下:
 

简单双口RAM


Ture Dual-port RAM(真双口RAM),有A和B两组接口,每一组接口都可以完成读和写操作,接口如下:
 

真双口RAM


l Single-port ROM
l Dual-port ROM
ECC Options:只有Simple Dual-port RAM时,该选项才可用,IP核支持内置的汉明纠错功能(Built-in Hamming Error Correction Capability(BuiltIn ECC)),且支持数据位宽小于64位的软汉明纠错(soft ECC)。
Write Enable:设置是否启用字节写入,启用时,字节位宽可选为8位(无奇偶校验位)和9位(有奇偶校验位),此时数据位宽应是字节位宽的倍数,默认情况下不启用。
Algorithm Options:选择实现的内存算法。
Minimum Area Algorithm:使用最少数量的原语生成IP核;
Low Power Algorithm:低功耗算法,在读或写操作期间启用最小数量的块RAM原语;
Fixed Primitive Algorithm:固定单元算法,连接一个基本内存单元来生成IP核,在下拉列表中选择要使用的单元类型。
这里选用Simple Dual-port RAM,其余保持默认。
配置端口A:
Write Width:写入位宽16(RGB565)
Read Width:读出位宽,读写位宽可以不一致,IP核支持的读写位宽比例有:1:32、1:16、1:8、1:4、1:2、1:1、2:1、4:1、8:1、16:1、32:1。
Write Depth:写入深度65536(显示256x256图像)
Write Width:写入位宽16(RGB565)
 

端口A


配置端口B:
        Read Depth:读出深度
        Operating Mode:选项有Write First、Read First和No Change三种模式。
        Enable Port Type:端口使能控制,可以使用ENA引脚控制,也可以选择always Enabled
        Port A Optional Output Rregisters:选择是否在输出端添加寄存器
        Port A Output Reset Options:配置复位信号
        READ Address Change A:这个功能只在UltraScale设备上使用。
        RAM写优先(Write First)时序如下,在ENA信号拉高后,在时钟上升沿检测ADDRA地址为aa,而WEA写使能为低,DOUTA就输出地址aa对应的数据;在第二个时钟上升沿,ENA为高,WEA信号为高,执行写操作,地址为bb,就将DINA上的数据1111写入存储中,并把DINA上的数据送到DOUTA上进行输出;在第三个时钟上升沿依然如此,将DINA数据写入存储同时将数据送到DOUTA上进行输出;第四个上升沿,ENA为高,WEA为低,执行读操作,地址为dd,就读取存储器地址dd中的数据送到DOUTA上进行输出。
        RAM读优先时序如下:在ENA和WEA同时为高时,输出的数据是存储器存储的数据,同时,也会把新写入的数据替换掉存储器中的对应地址的数据。
        RAM No Change模式:在ENA和WEA同时为高时,只进行写操作,不进行读操作,也就是说DOUTA保持前一拍数据,直到WEA为低。
        Pipeline Stages within Mux:输出端Mux流水线级
        Memory Initialization:选择是否使用本地初始化.coe文件初始化存储空间。以及是否使用默认值初始化内存。
        Structural/UNISIM Simulation Model Options:选择发生碰撞时由结构仿真模型生成的警告消息和输出的类型。
点击OK生成IP核。

三、单RAM读写操作

//==========================================================================
// --- 名称 : ram_ctrl.v
// --- 作者 : 木子
// --- 日期 : 2024-8-30
// --- 描述 : 单个ram读写操作
//==========================================================================

module ram_ctrl
//---------------------<端口声明>-------------------------------------------
(
input  wire             clk                 , //时钟,50Mhz
input  wire             rst_n               , //复位,低电平有效
input  wire [7:0]       din                 , //写入的数据
output wire [7:0]       dout                  //读出的数据
);
//---------------------<信号定义>-------------------------------------------
reg                     wr_en               ; //写使能
reg  [7:0]              wr_addr             ; //写地址
reg  [7:0]              rd_addr             ; //读地址

//--------------------------------------------------------------------------
//--   ram例化
//--------------------------------------------------------------------------
ram_256x8 u_ram_256x8
(
    .clock              (clk                ),
    .wren               (wr_en              ),
    .wraddress          (wr_addr            ),
    .data               (din                ),
    .rdaddress          (rd_addr            ),
    .q                  (dout               )
);

//--------------------------------------------------------------------------
//--   产生写使能
//--------------------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_en <= 1'b1;
    else if(wr_addr==8'd255)
        wr_en <= 1'b0;
    else if(rd_addr==8'd255)
        wr_en <= 1'b1;
end

//--------------------------------------------------------------------------
//--   产生写地址
//--------------------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_addr <= 8'd0;
    else if(wr_en==1)
        wr_addr <= wr_addr+1'd1;
    else
        wr_addr <= 8'd0;
end

//--------------------------------------------------------------------------
//--   产生读地址
//--------------------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_addr <= 8'd0;
    else if(wr_en==0)
        rd_addr <= rd_addr+1'd1;
    else
        rd_addr <= 8'd0;
end



endmodule

四、双RAM乒乓操作

        RAM的简单读写比较简单,平常也用不太到,比较重要的是异步双口RAM实现乒乓操作。

//==========================================================================
// --- 名称 : ram_pp.v
// --- 作者 : 木子
// --- 日期 : 2024-08-30
// --- 描述 : 异步双口ram乒乓操作
//==========================================================================

module ram_pp
//---------------------<端口声明>-------------------------------------------
(
input  wire             clk                 , //时钟,50Mhz
input  wire             rst_n               , //复位,低电平有效
input  wire [7:0]       din                 , //输入的数据
output wire [7:0]       dout                  //输出的数据
);
//---------------------<信号定义>-------------------------------------------
//ram_a
reg                     wr_en_a             ;
reg     [9:0]           wr_addr_a           ;
reg     [9:0]           rd_addr_a           ;
wire    [7:0]           dout_a              ;
//ram_b
reg                      wr_en_b            ;
reg     [9:0]            wr_addr_b          ;
reg     [9:0]            rd_addr_b          ;
wire    [7:0]            dout_b             ;
//缓一拍
reg                      wr_en_a_r0         ;

//--------------------------------------------------------------------------
//--   ip核例化
//--------------------------------------------------------------------------
//ram_a
ram_1024x8 u_ram_1024x8_a
(
  .clka                 (clk                ), // input  clka
  .wea                  (wr_en_a            ), // input  [0 : 0] wea
  .addra                (wr_addr_a          ), // input  [9 : 0] addra
  .dina                 (din                ), // input  [7 : 0] dina
  .clkb                 (clk                ), // input  clkb
  .addrb                (rd_addr_a          ), // input  [9 : 0] addrb
  .doutb                (dout_a             )  // output [7 : 0] doutb
);
//ram_b
ram_1024x8 u_ram_1024x8_b
(
  .clka                 (clk                ), // input  clka
  .wea                  (wr_en_b            ), // input  [0 : 0] wea
  .addra                (wr_addr_b          ), // input  [9 : 0] addra
  .dina                 (din                ), // input  [7 : 0] dina
  .clkb                 (clk                ), // input  clkb
  .addrb                (rd_addr_b          ), // input  [9 : 0] addrb
  .doutb                (dout_b             )  // output [7 : 0] doutb
);

//--------------------------------------------------------------------------
//--   ram_a
//--------------------------------------------------------------------------
// 写使能
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_en_a <= 1'b1;
    else if(rd_addr_a=='d1023)
        wr_en_a <= 1'b1;
    else if(wr_addr_a=='d1023)
        wr_en_a <= 1'b0;
end

// 写地址
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_addr_a <= 'd0;
    else if(wr_addr_a=='d1023)
        wr_addr_a <= 'd0;
    else if(wr_en_a==1'b1)
        wr_addr_a <= wr_addr_a + 1'b1;
end

// 读地址
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_addr_a <= 'd0;
    else if(rd_addr_a=='d1023)
        rd_addr_a <= 'd0;
    else if(wr_en_a==1'b0)
        rd_addr_a <= rd_addr_a + 1'b1;
end

//--------------------------------------------------------------------------
//--   ram_b
//--------------------------------------------------------------------------
// 写使能
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_en_b <= 1'b0;
    else if(wr_addr_a=='d1023)    //关键之处!!!
        wr_en_b <= 1'b1;
    else if(wr_addr_b=='d1023)
        wr_en_b <= 1'b0;
end

// 写地址
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_addr_b <= 'd0;
    else if(wr_addr_b=='d1023)
        wr_addr_b <= 'd0;
    else if(wr_en_b==1'b1)
        wr_addr_b <= wr_addr_b + 1'b1;
end

// 读地址
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_addr_b <= 'd0;
    else if(rd_addr_b=='d1023)
        rd_addr_b <= 'd0;
    else if(wr_en_b==1'b0)
        rd_addr_b <= rd_addr_b + 1'b1;
end

//--------------------------------------------------------------------------
//--   wr_en_a缓一拍时序才对齐
//--------------------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_en_a_r0 <= 0;
    else
        wr_en_a_r0 <= wr_en_a;
end

//--------------------------------------------------------------------------
//--   数据输出
//--------------------------------------------------------------------------
assign dout = (wr_en_a_r0==0) ? dout_a : dout_b;



endmodule

五、单口,伪双口,真双口的区别

1.单口:只有一组数据线和地址线,只生成一个addr,因此不能同时进行读写。

2.双口:拥有两组数据线和地址线,可以生成wr_addr和rd_addr,因此可以同时进行读写。

    ①伪双口:一个端口读,一个端口写。

    ②真双口:两个端口都能读能写。

ps:

无论是单口、伪双口还是真双口,他们都只使用一块Memory,真双口其实是两组地址对同一块Memory进行读写,如果真双口的两端口同时对同一地址进行写入数据,那实际情况是未知(仿真也不可信)。

六、ROM、RAM和FIFO的区别

1.ROM有地址,只能读而不能写。用初始化文件mif/ceo将内容存进去,读取不会使得数据减少消失。

2.RAM有地址,可以进行寻址读写,数据写进去后,读取不会使得数据减少消失。

3.FIFO没有地址,只能是先进先出,数据写进去后,读取会使得数据减少消失,读一个少一个。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值