vivado ram ip实验与设计实验

RAM可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度是由时钟频率决定的RAM:随机访问内存
随机访问内存(RAM)相当于PC机上的移动存储,用来存储和保存数据的。在任何时候都可以读写,RAM通常用作操作系统或其他正在运行的程序的临时存储介质(可称作系统内存)。不过,当电源关闭时RAM不能保留数据,如果需要保存数据,就必须把它们写入到一个长期的存储器中(例如硬盘)。正因为如此,有时也将RAM称作"可变存储器"。RAM内存可以进一步分为静态RAM(SRAM)和动态内存(DRAM)两大类。

SRAM:静态随机存取存储器

静态随机存取存储器(Static Random-Access Memory,SRAM)是随机存取存储器的一种。所谓的“静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。相对之下,动态随机存取存储器(DRAM)里面所储存的数据就需要周期性地更新。然而,当电力供应停止时,SRAM储存的数据还是会消失(被称为volatile memory),这与在断电后还能储存资料的ROM或闪存是不同的。

DRAM:动态随机存取存储器

SRAM不需要刷新电路即能保存它内部存储的数据。而DRAM(Dynamic Random Access Memory)每隔一段时间,要刷新充电一次,否则内部的数据即会消失,因此SRAM具有较高的性能,但是SRAM也有它的缺点,即它的集成度较低,功耗较DRAM大 ,相同容量的DRAM内存可以设计为较小的体积,但是SRAM却需要很大的体积。同样面积的硅片可以做出更大容量的DRAM,因此SRAM显得更贵。。

RAM主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。在资源允许的范围内,允许灵活地配置ram
Vivado软件自带BMG IP核,可以配置成RAM或者ROM(只允许进行读操作)。通过对这些bram存储器模块进行配置,可以实现ram、移位寄存器、rom以及fifo缓冲器等各种存储器的功能。
我们首先进行单端口ram ip的调用实验,明白它的作用和使用方式;再独立进行ram的编写实验
单端口RAM:只有1个端口,共用读写通道,读写不能同时进行
伪双端口RAM:2个通道,分别进行读写
真双端口RAM:2个通道,都可以读写

单端口ip实验

首先调用vivado ip进行单端口实验:
要使用ram ip,要在ip catalog里面搜索ram,并选中block memory generator

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

修改端口设置
在这里插入图片描述
然后就可以通过生成的veo文件的模板调用它

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
);

这里我们要用逻辑分析仪进行分析ram的读写,因为modelsim等仿真软件是看不到相关信号的,所以还要在代码中加入ila ip的调用
在这里插入图片描述
配置如下:

在这里插入图片描述
在这里插入图片描述

读写模块代码:

module ram_wr(
 input            clk      ,
 input            rst_n    ,
 input   [7:0]    r_data   ,  //ram读数据

 output reg [7:0] w_data   ,  //ram写数据
 output           ram_write_sel   ,  //ram读写选择
 output reg [4:0] addr ,  //ram读写地址
 output           ram_en   ); //ram使能信号

//读写计数器
reg [5:0] wr_cnt;

//0-31为写,32到63为读
assign ram_write_sel = (wr_cnt<=6'd31 && ram_en)? 1'b1 : 1'b0;
assign ram_en=rst_n;

//产生ram读写数据
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n)
        w_data <= 0;
    else if((wr_cnt <= 6'd31)&&ram_en)
        w_data <= + 8'd1;
    else 
        w_data <= 0;
end

//读写计数器,计数范围为0-63
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n)
        wr_cnt <= 0;
    else if(wr_cnt==6'd63)
        wr_cnt <= 0;
    else
        wr_cnt <= + 1;
end

//读写地址信号,范围0-31
always @(posedge clk or negedge rst_n) begin 
    if(!rst_n)
        addr <= 0;
    else addr<=wr_cnt[4:0];
end



endmodule

顶层模块:

module ip_ram(
   input sys_clk,
   input sys_rst);

    wire [7:0] r_data;
    wire [7:0] w_data;
    wire       ram_en;
    wire       ram_sel;
    wire [4:0] addr;

   ram_wr wr_inst(
    .clk     (sys_clk),
    .rst_n   (sys_rst),
    .r_data  (r_data),
    .w_data  (w_data),
    .ram_write_sel  (ram_sel),
    .addr(addr),
    .ram_en  (ram_en)  );

  blk_mem_gen_0 mem_gen_0 (
  .clka(sys_clk),            // input wire clka
  .rsta(sys_rst), 
  .ena(ram_en),              // input wire ena
  .wea(ram_sel),              // input wire [0 : 0] wea
  .addra(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
);
ila_ram test_ram (
	.clk(sys_clk), // input wire clk
	.probe0(ram_en), // input wire [0:0]  probe0  
	.probe1(ram_sel), // input wire [0:0]  probe1 
	.probe2(addr), // input wire [4:0]  probe2 
	.probe3(w_data), // input wire [7:0]  probe3 
	.probe4(r_data) // input wire [7:0]  probe4
);
endmodule


rtl视图
在这里插入图片描述
然后将比特流文件下载到开发板上进行调试
在这里插入图片描述
在这里插入图片描述
由于我们选择了ram ip写优先,所以波形就是这样的,也可以把它调整成为不改变:
在这里插入图片描述
这样波形图就更合理了
在这里插入图片描述
在这里插入图片描述

双端口ram代码编写

这里我们采用三态门进行数据的输入输出,代码编写如下:

module dp_sram
#(   parameter           ADDR_WIDTH  =   4,
parameter           DATA_WIDTH  =   8,
parameter DEPTH = 2**ADDR_WIDTH )
(/*autoarg*/
   // Outputs
   // Inputs
   input clka, input clkb, 
   input csen, 
   inout   [DATA_WIDTH-1:0]data_a,  inout   [DATA_WIDTH-1:0]data_b,
   input  [ADDR_WIDTH-1:0]addra,input [ADDR_WIDTH-1:0]  addrb, 
   input wrena,input  wrenb,
   input rdena,input rdenb
   );
reg     [DATA_WIDTH-1:0]   mem[DEPTH-1:0];
reg [DATA_WIDTH - 1 : 0] mid_data_a;             //中间缓存的寄存器
reg [DATA_WIDTH - 1 : 0] mid_data_b;             //中间缓存的寄存器
assign data_a = (csen &rdena &! wrena)? mid_data_a: 'hz;
assign data_b = (csen & rdenb &! wrenb)? mid_data_b: 'hz;
always @(posedge clka)begin
    if(wrena && csen)
    mem[addra] <= data_a;   
else  ;    
end
always @(posedge clkb)begin
    if(wrenb && csen)
    mem[addrb] <= data_b;
else ;
end
always@(posedge clka) begin             
    if(!wrena && csen)                     
        mid_data_a <= mem[addra];   
   else ;
end
always@(posedge clkb) begin             
    if(!wrenb && csen)                       
        mid_data_b <= mem[addrb];   
   else ;
end

endmodule 


这个模块有对应的信号输出,可以用modelsim进行仿真,testbench文件编写如下:

`timescale 1ns / 1ps
// Engineer: Reborn Lee
// Module Name: ram_tb
module ram_tb(
    );
	parameter ADDR_WIDTH = 4;         //地址位宽
	parameter DATA_WIDTH = 16;        // 数据位宽
	parameter DEPTH = 2**ADDR_WIDTH;   //ram 列表长度
	reg i_clk;                          //时钟
	reg [ADDR_WIDTH - 1 : 0] addr_a;       // ram 地址参数
    reg [ADDR_WIDTH - 1 : 0] addr_b; 
	wire [DATA_WIDTH - 1 : 0] data_a;      //这个是一个零时变量法,位宽与ram 一样,用于存放缓存的数据
	wire [DATA_WIDTH - 1 : 0] data_b;
	reg cs;                              // 这是ram 使能 信号,表示是否操作这个ram 
	reg wrena;                              // 写使能
	reg wrenb;
	reg rdena;                              // output enable,输出使能时RAM读取的结果才能输出
    reg rdenb;
	reg [DATA_WIDTH-1:0] tb_data;        //这个是一个零时变量法,位宽与ram 一样,用于存放缓存的数据
	//generate system clock
	initial begin                        
		i_clk = 0;
		forever begin                                               
			# 5 i_clk = ~i_clk;         
		end
	end

	assign data_a = !rdena ? tb_data : 'hz;   // 写数据,
	assign data_b = !rdenb ? tb_data : 'hz;
   // assign data =  tb_data ;
	initial begin
    {cs, wrena,wrenb, addr_a,addr_b, tb_data, rdena,rdenb} = 0;    //一开始都置零
 
    repeat (2) @ (posedge i_clk);       //先来两个时钟周期
    //write test
    //每个时钟周期,向 ram 中写一个随机数据,数据写的时候不能读 ,oe=0  输出使能0
    for (integer i = 0; i < 2**ADDR_WIDTH/2; i= i+1) begin
      repeat (1) @(negedge i_clk) 
	  addr_a = i; 
	  wrena = 1; cs =1; 
	  rdena = 0; tb_data = $random;
    end
	for (integer i = 2**ADDR_WIDTH/2; i < 2**ADDR_WIDTH; i= i+1) begin
		repeat (1) @(negedge i_clk) 
		addr_b = i; 
		wrenb = 1; cs =1; 
		rdenb = 0; tb_data = $random;
	  end

    //read test
    //两次读写之间 有两个时钟周期的延时
    repeat (2) @ (posedge i_clk);    
     //每个时钟周期,向 ram 中读一个随机数据,数据读的时候不能写 ,wr=0  写使能 0 
    for (integer i = 0; i < 2**ADDR_WIDTH/2; i= i+1) begin
      repeat (1) @(posedge i_clk) addr_a = i; 
	  wrena = 0; cs = 1; rdena = 1;
    end
	for (integer i =  2**ADDR_WIDTH/2; i < 2**ADDR_WIDTH; i= i+1) begin
		repeat (1) @(posedge i_clk) addr_b = i; 
		wrenb = 0; cs = 1; rdenb = 1;
	  end
    #20 $finish;
  end
    //下面是函数调用的接口
    
	dp_sram #(
			.ADDR_WIDTH(ADDR_WIDTH),
			.DATA_WIDTH(DATA_WIDTH),
			.DEPTH(DEPTH)
	) dp_sram_inst (
			.i_clk (i_clk),
			.addr_a  (addr_a),
			.addr_b  (addr_b),
			.data_a  (data_a),
			.data_b  (data_b),
			.cs    (cs),
			.wrena    (wrena),
			.wrenb    (wrenb),
			.rdena    (rdena),
			.rdenb    (rdenb)
		);

endmodule

仿真结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值