ISE使用中RAM IP核配置及ram测试(两种测试)

简单总结ISE中RAM的ip核配置过程以及相关的端口。

分类

ram分为分布式ram(distributed ram)以及块ram(block ram)
前者是自己用寄存器搭建的,这里理解可以转至Vivado使用技巧(27):RAM编写技巧
简单理解块ram就是自己用寄存器写出来的储存单元,仅仅用于储存比较少量的数据。优点是dram不需要时钟线来控制,可以直接用组合逻辑进行控制。bram是fpga中定制的ram资源,控制起来需要用时钟线,以及其他UI界面设定的io接口进行配置。

定制过程及说明

  1. 首先进入页面,打开进行选择要生成的ip核
  2. 选择你要定制的类型
    在这里插入图片描述
    可以选择的类型有如下五个,其中两种是ROM,三种是RAM。这里只总结RAM,ROM前面有进行简单的总结,后面会结合所学的,对这些全部进行注意点的总结。接下来是几种RAM的简介。
    在这里插入图片描述
single - port RAM

简单单口RAM
在这里插入图片描述

端口说明

ADDRA 为地址线
DINA 为数据输入端
ENA 为可选端口,在其为高电平时,才使能。
WEA 为写使能“当其为高电平是,DINA数据才会被写入对应的地址,这里[0:0],仿真的时候会发现,这里的数据不同于时钟线,而是数据。”
DOUTA 为A输出端
CLKA 为时钟线

Simple Dual Port Ram

在这里插入图片描述
这里只是多出来了个B端口,其中B用来读数据。
这里就会遇到一个问题,当同时写入数据和读出数据的时候该怎么进行选择呢,这里会有用户自行设定,会在接下来的页面中进行介绍。(这里可以分为三种模式)

True Dual-port RAM

在这里插入图片描述
图中上边的端口A和下边的端口B都支持读写操作,WEA、WEB信号为高时进行写操作,低为读操作。同时它支持两个端口读写操作的任何组合:两个同时读操作、两个端口同时写操作或者在两个不同的时钟下一个端口执行写操作,另一个端口执行读操作。

同时这里有一个选择框,问是否使用同一个时钟(CLKA,CLKB)
在这里插入图片描述

  1. 数据端口配置
    这里可以配置写入的宽度和深度。在这里插入图片描述
    同时可以尽行不同宽度的读取。就是说写入的宽度不同于读取的宽度。系统会对深度进行自动设定。
    在这里插入图片描述

当选择真正双口ram的实时候,会出现这里的三个设计端口。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 其他配置
    后面还有几页,这里笔者能力有限,以后进行补充。

ram测试(1)

代码主要是描述了读写时间的控制,然后对写入的内容进行控制。这里测试代码来源于开拓者资料(我买的黑金板子,那个资料,,无语-----)
下是测试结果,可以看到,ram读出的数据需要延时一个时钟周期。就是说,在读地址位为1的时候,它读出的数据其实是地址0里边的数据。
在这里插入图片描述
(解决输出延时一个周期的方法是,让读地址提前一个时钟周期,读地址去读,读地址的延时作为数据的观测)如下图
在这里插入图片描述

代码书写

这里的读数据使能我没有用,具体代码如下

module ip_ram(
	input sys_clk,
	input sys_rst_n
    );

wire [0:0] ram_wr_en ;		//ram写使能
wire ram_rd_en ;		//ram读使能
wire [4:0] ram_addr ;	//ram读写地址
wire [7:0] ram_wr_data;	//ram写数据

wire [7:0] ram_rd_data; //ram读数据

ram_rw A (
    .clk(sys_clk), 
    .rst_n(sys_rst_n), 
    .ram_rd_data(ram_rd_data), 
    .ram_wr_en(ram_wr_en), 
    .ram_rd_en(ram_rd_en), 
    .ram_addr(ram_addr), 
    .ram_wr_data(ram_wr_data)
    );

ram_test B (
  .clka(sys_clk), // input clka
  .wea(ram_wr_en), // input [0 : 0] wea
  .addra(ram_addr), // input [4 : 0] addra
  .dina(ram_wr_data), // input [7 : 0] dina
  .clkb(sys_clk), // input clkb
  .addrb(ram_addr), // input [4 : 0] addrb
  .doutb(ram_rd_data) // output [7 : 0] doutb
);


endmodule

module ram_rw(
	clk,rst_n,ram_rd_data,
	ram_wr_en,ram_rd_en,ram_addr,ram_wr_data
    );
input 				clk				; 		//系统时钟
input 				rst_n			;		//复位信号
input 		[7:0] 	ram_rd_data		;		//ram读数据
output  	[0:0]	ram_wr_en		;		//写使能信号
output 		 		ram_rd_en		;		//读使能信号
output  reg [4:0]   ram_addr		;		//ram写地址
output 	reg [7:0]	ram_wr_data		;		//ram写数据 


reg 	[5:0] 	rw_cnt			;		//读写控制计数器
//数据计数在0-31之内,ram写使能为高电平
assign ram_wr_en = ((rw_cnt >= 6'd0) && (rw_cnt <= 6'd31)) ? 1'b1 : 1'b0;
//数据计数在32-63之内,ram读使能为高电平
assign ram_rd_en = ((rw_cnt >= 6'd32) && (rw_cnt <= 6'd62)) ? 1'b1 : 1'b0;

always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)
		rw_cnt <= 6'd0;
	else 
		if(rw_cnt == 6'd63)
			rw_cnt <= 6'd0;
		else 
			rw_cnt <= rw_cnt + 6'd1;	
end
//写入的数据,在写使能为高电平的时候,写入的数据加一
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)
		ram_wr_data <= 8'd0;
	else 
		if((rw_cnt >= 6'd0) && (rw_cnt <= 6'd31))
			ram_wr_data <= ram_wr_data + 8'b1;
		else 
			ram_wr_data <= 8'd0;
end
//读地址信号控制,读地址一直循环在所有地址之间
always @ (posedge clk or negedge rst_n)
begin
	if(!rst_n)
		ram_addr <= 5'd0;
	else 
		if(ram_addr == 5'd31)
			ram_addr <= 5'd0;
		else 
			ram_addr <= ram_addr + 5'd1;
end

endmodule

ram测试(2)A

测试八位写入,十六位读出,这里只需要控制写入数据,读取数据是只需要控制地址位即可。

在这里插入图片描述
这里只有一个数据有问题,就是在ram里的第15个数据,也就是对应的30和31次写入的数据不对,同时可以看出,读出数据的时候也是要延时一个时钟周期。而且这里读出的数据,例如01,写入0f 02 写入05
这样读出的第一位数据是050f
换了一个顺序,将后写入的数据作为高位?!
所以在次基础上,增加一个测试,见ram测试(2)B

代码书写

module ip_ram2(sys_clk,sys_rst_n
    );
input 		sys_clk				;
input 		sys_rst_n			;

reg   [0:0] wea;
reg   [4:0] wr_addr;
reg   [3:0] rd_addr;
wire  [7:0] wr_data;
reg   [0:0] cnt_en ;
//写地址,写入当有wea信号来的时候,地址位+1
always @ (posedge sys_clk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		wr_addr <= 5'd0;
	else 
		if(wr_addr == 5'd31)
			wr_addr <= 5'd0;
		else 
			if(wea)
				wr_addr <= wr_addr + 1'b1;
			else 
				wr_addr <= wr_addr;
end
//读地址控制
always @ (posedge sys_clk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		rd_addr <= 4'd0;
	else 
		rd_addr <= rd_addr + 4'd1;
end
//控制写使能信号
always @ (posedge sys_clk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		wea <= 1'b0;
	else 
		if(cnt_en == 1'b1)
			wea <= 1'b1;
		else 
			wea <= 1'b0;
end
//cnt_en计数
always @ (posedge sys_clk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		cnt_en <= 1'b0;
	else 
		if(cnt_en == 1'b1)
			cnt_en <= 1'b0;
		else 
			cnt_en <= 1'b1;
end
assign wr_data = ( wr_addr%2 ) ? 8'h5 : 8'hf;
ram_test2 A (
  .clka(sys_clk), // input clka
  .wea(wea), // input [0 : 0] wea
  .addra(wr_addr), // input [4 : 0] addra
  .dina(wr_data), // input [7 : 0] dina
  .clkb(sys_clk), // input clkb
  .addrb(rd_addr), // input [3 : 0] addrb
  .doutb(doutb) // output [15 : 0] doutb
);
endmodule

ram测试(2)B

这里对其初始化,直接读出数据。
在这里插入图片描述在这里插入图片描述
这里可以看出,确实是8位写入,16位读出的时候,第0位读出的数据是1100
写入时候,0写入的时候,写入的是00,1写入的时候,写的数据时11
读出的时候。可以看出其变换(同样延时一个时钟周期读出数据)

代码书写

module ip_ram3(
	clk
    );
input clk;
reg [1:0] addrb = 2'd0;
always @ (posedge clk )
begin
	addrb <= addrb + 1'b1;
end
ram_dual_test A (
  .clka(clk), // input clka
  .wea(wea), // input [0 : 0] wea
  .addra(addra), // input [2 : 0] addra
  .dina(dina), // input [7 : 0] dina
  .clkb(clk), // input clkb
  .addrb(addrb), // input [1 : 0] addrb
  .doutb(doutb) // output [15 : 0] doutb
);

endmodule

以上就完成了简单的配置以及简单的测试。当然,也可以对其进行初始化。类似rom操作。
参考博文:
双口RAM及Vivado RAM IP核的使用
Vivado使用技巧(27):RAM编写技巧
感谢~

  • 12
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值