FPGA 单端口RAM IP核使用 vivado仿真

一、各类存储器简介

ROM:只读,只有读接口(读地址、读数据)

RAM:可读可写,有读接口(读地址、读数据)和写接口(写使能、写数据、写地址),默认任何时刻都能读,没有读使能,大小和位宽查手册,需要持续供电才能将数据保存在其中(易失性存储器),断电数据丢失

  • 单端口RAM:读写共用一个数据通道,读写不能同时进行
  • 伪双端口RAM:两个数据通道,一个用来读一个用来写
  • 真双端口RAM:两个数据通道,都可以用来读或写

DRAM:动态随机存取存储器,数据存储在电容器中,通过保持电荷实现数据存储(比如电容器充电和放电分别为1和0),价格低,消耗功率高,最常用作计算机的主存储器,需要不断刷新(由于电容器内部用于分隔导电板的电介质不是完美的绝缘体,电容器易放电导致电荷泄漏),以同步或异步模式运行,存储容量为1GB-16GB

SRAM:静态随机存取存储器,数据存储在晶体管中,通过改变晶体管的导通状态进而改变电平高低,需要恒定的功率流,速度快,用于高速缓存,存储容量为1MB-16MB

SDRAM:同步动态随机存储器(与CPU频率同步、存储阵列需要不断刷新来保证数据不丢失、自由指定地址进行数据读写),通过电容充放电实现数据存储,内部分为多个bank,通过流水线操作提高速度(一个bank处于预充电状态,正在经历访问延迟时,另一个bank可进行读取),存储容量大、速度快、价格低,控制逻辑复杂、对时序要求高

DDR:双倍速率同步动态随机存储器,命令和操作只在时钟的上升沿发生,数据传输在时钟上升沿和下降沿发生,每个时钟周期发送两倍数据

FIFO和RAM的差别:FIFO是先入先出,没有读写地址,只能按顺序读写数据,RAM可以读写任意地址,FIFO常用于数据传输通道中,用于缓存数据,避免数据丢失,RAM常用于存储指令或者中间的数据

二、原理

IP核配置图

信号名称信号功能
dina端口a的写数据信号
addr端口a的读写地址信号
wea端口a的写使能信号,高电平为写,低电平为读
ena端口a的使能信号,高电平为使能端口a,低电平端口a被禁止,取消此信号后端口a一直有效(可选信号)
rsta端口a的复位信号(可选信号)
regcea端口a输出寄存器使能信号,高电平douta保持最后一次输出(可选信号)
clka端口a的时钟信号
douta端口a数据输出

三、vivado仿真

新建工程:打开vivado-Quick Start-Create Project-Next-输入工程名和位置(注意工程名和位置中都不能出现中文和空格)-勾选Create project subdirectory(为工程在指定存储路径下建立独立的文件夹)-勾选RTL Project-勾选Do not specifysources at this time(此时不定义源文件)-选择FPGA开发板(我用的是xc7k325tfbg676-3)-Next-Finish

创建RAM IP核:Flow Navigator-IP Catalog-Search:block memory-Block Memory Generator

配置IP核:component name(器件名称,默认即可,不用修改)-basic-interface type(接口类型,默认native)-memory type选择single port ram-write enable中取消勾选字节写使能byte write enable-algorithm options-algorithm选最小面积minimum area-ok

 port A options-memory size-write width写数据位宽选择8-write depth写深度选择32-operating mode选no change-enable port type选use ena pin-port A optional output registers都不勾选-port A output reset options勾选RSTA pin-output reset value为0-ok

 创建模块ram_rw.v:

 project manager-add sources-add or create design sources-next-create file-输入文件名ram_rw.v-ok-finish

module ram_rw(
    input            clk,
    input            rst_n,//端口复位,为0时复位
    input      [7:0] r_data,//读数据
    output reg [7:0] w_data,//写数据
    output           ram_we,//写使能
    output reg [4:0] ram_addr,//读写地址
    output           ram_en//端口使能
    );


(* DONT_TOUCH = "TRUE" *) reg [5:0] wr_cnt;//读写计数,因为没有规定输入输出,所以加上前缀括号

assign ram_en = rst_n;//端口使能

assign ram_we = (wr_cnt<=6'd31 && ram_en==1)? 1'b1 : 1'b0;//写使能:读写计数器在0-31为写,32-63为读,6'd31为6位四进制数,数值为31

//读写计数信号设计
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n)
        wr_cnt <= 0;//复位时读写计数为0
    else if(wr_cnt==6'd63)
        wr_cnt <= 0;//读写计数为63,赋值为0,准备重新计数
    else
        wr_cnt <= wr_cnt + 1;//其他情况,每次计数加1
end

//读写地址信号设计
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n)
        ram_addr <= 0;//复位时读写地址为0
    else if(ram_addr==5'd31)
        ram_addr <= 0;//读写地址为31,赋值为0,重新开始计数
    else 
        ram_addr <= ram_addr + 1;//其他情况,每次地址加1
end

//写数据信号设计
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n)
        w_data <= 0;//复位时写数据为0
    else if(wr_cnt <= 6'd31)
        w_data <= w_data + 1;//读写计数在0-31为写
    else 
        w_data <= 0;//其他情况,写数据为0
end

//读数据信号设计
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n)
        r_data <= 0;//复位时读数据为0
    else if(wr_cnt >= 6'd32 && wr_cnt <= 6'd63)
        r_data <= r_data + 1;//读写计数在32-63为读
    else 
        r_data <= 0;//其他情况,读数据为0
end


endmodule

本部分的功能:对一个数据位宽为8、深度为32的单端口RAM 进行读写操作设计,注意一个always设计一个信号,对于每个信号的设计,要覆盖到其所有情况 

例化IP核blk_mem_gen_0及ram_rw 模块:

sources-IP sources-找到.veo文件,查看例化ram IP核的模板

blk_mem_gen_0 your_instance_name (
  .clka(clka),            // input wire clka
  .rsta(rsta),            // input wire rsta
  .ena(ena),              // input wire ena
  .wea(wea),              // input wire [0 : 0] wea
  .addra(addra),          // input wire [4 : 0] addra
  .dina(dina),            // input wire [7 : 0] dina
  .douta(douta),          // output wire [7 : 0] douta
  .rsta_busy(rsta_busy)   // output wire rsta_busy
);

创建顶层模块ipram:

module ipram(
    input sys_clk,
    input sys_rst
    );

    wire [7:0] r_data;
    wire [7:0] w_data;
    wire       ram_en;
    wire       ram_we;
    wire [4:0] ram_addr;


   ram_rw ram_rw_inst(
    .clk     (sys_clk),//例化本质上就是调用的过程,顶层模块ipram调用读写模块ram_rw,括号内为顶层模块的信号名
    .rst_n   (sys_rst),
    .r_data  (r_data),
    .w_data  (w_data),
    .ram_we  (ram_we),
    .ram_addr(ram_addr),
    .ram_en  (ram_en)  );

  blk_mem_gen_0 blk_mem_gen_1 (//顶层模块ipram调用ram IP核blk_mem_gen_0
  .clka      (sys_clk),            // input wire clka
  .rsta      (sys_rst),            // input wire rsta
  .ena       (ram_en),              // input wire ena
  .wea       (ram_we),              // input wire [0 : 0] wea
  .addra     (ram_addr),          // input wire [4 : 0] addra
  .dina      (w_data),             // input wire [7 : 0] dina
  .douta     (r_data)             // output wire [7 : 0] douta
  //.rsta_busy(rsta_busy)      // output wire rsta_busy顶层模块没有用到此信号
);



endmodule

仿真程序:

module tb_ram_ip(

    );

(* DONT_TOUCH = "TRUE" *) reg sys_clk;//输入信号
(* DONT_TOUCH = "TRUE" *) reg sys_rst;

ipram ipram_inst(
    .sys_clk(sys_clk),//仿真模块tb_ram_ip调用顶层模块ipram
    .sys_rst(sys_rst)
);
initial begin 
    sys_clk = 0;
    sys_rst = 0;
    #10//等待复位完成
    sys_rst = 1;
    #256
    $stop;
end
always #5 sys_clk = ~sys_clk;//10ns一个周期,产生100MHz时钟源



endmodule

参考文献:

一文看懂SDRAM、DRAM和DDR的差别

【Vivado】ram ip核的使用_想学fpga的小猪同学的博客-CSDN博客_vivado移位寄存器FIFO RAM的差异与共同点_IC小鸽的博客-CSDN博客_fifo和ram的区别

Vivado 双口RAM IP核的使用_耐心的小黑的博客-CSDN博客

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值