IP 核之RAM实验

目录:

    • 1.IP核 RAM简介
    • 2.实验1: 配置单端口 RAM
      • 1)实验任务
      • 2)创建工程并添加ram ip
      • 3)编写ram_rw.v
      • 4)编写顶层文件
      • 5)编写激励文件
      • 6)仿真测试
      • 7)ILA测试
    • 3.实验2: 配置伪双端口 RAM
      • 1)创建工程并添加ram ip
      • 2)伪双端口RAM的端口定义和时序
      • 3)编写测试程序
      • 4)编写激励文件
      • 5)仿真测试
      • 6)ILA测试

1.IP核 RAM简介

RAM 的英文全称是 Random Access Memory,即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度是由时钟频率决定的。RAM 主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。

Xilinx 7 系列器件具有嵌入式存储器结构,它由一列列 BRAM(块 RAM)存储器模块组成,通过对这些 BRAM 存储器模块进行配置,可以实现各种存储器的功能,例如:RAM、移位寄存器、ROM 以及 FIFO 缓冲器。

Vivado 软件自带了 BMG IP 核(Block Memory Generator,块 RAM 生成器),可以配置成 RAM 或者ROM。这两者的区别是 RAM 是一种随机存取存储器,不仅仅可以存储数据,同时支持对存储的数据进行修改;而 ROM 是一种只读存储器,也就是说,在正常工作时只能读出数据,而不能写入数据。

BRAM 全部是真双端口 RAM(True Dual-Port ram,TDP),这两个端口都可以独立地对 BRAM 进行读/写。但也可以被配置成伪双端口 RAM(Simple Dual-Port ram,SDP)(有两个端口,但是其中一个只能读,另一个只能写)或单端口 RAM(只有一个端口,读/写只能通过这一个端口来进行)。单端口 RAM 只有一组数据总线、地址总线、时钟信号以及其他控制信号,而双端口 RAM 具有两 组数据总线、地址总线、时钟信号以及其他控制信号。

2.实验1: 配置单端口 RAM

1)实验任务

配置一个单端口的 RAM,然后对 RAM 进行读写操作,通过在 Vivado 自带的仿真器中观察波形是否正确,最后将设计下载到 Zynq 开发板中,并使用 ILA 对其进行在线调试观察。

IP 核之RAM实验_第1张图片

2)创建工程并添加ram ip

IP 核之RAM实验_第2张图片

IP 核之RAM实验_第3张图片图1 PICTURE ONE
IP 核之RAM实验_第4张图片图2 PICTURE TWO
IP 核之RAM实验_第5张图片图3 PICTURE TWO

3)编写ram_rw.v

/*
 * 当计数范围在 0~31 之间时,向 ram 中写入数据;
 * 当计数范围在 32~63 之间时,从 ram 中读出数据。
 */
module ram_rw(
	input 				clk,
	input 				rst_n,

	output				ram_wea,
	output				ram_en,	
	output	reg	[4:0]	ram_addr,
	output	reg	[7:0]	ram_wr_data,
	input		[7:0]	ram_rd_data
    );

reg [5:0]	cnt;

//计数器 范围(0~63)
always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
		cnt <= 6'd0;
	else if(cnt == 6'd63)
		cnt <= 6'd0;
	else
		cnt <= cnt + 1'b1;
end

//使能信号
assign ram_en = rst_n;

//读写信号
assign ram_wea = (cnt < 6'd32 && ram_en) ? 1'b1:1'b0;

//地址信号
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 + 1'b1;
end

//写信号的数据
always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
		ram_wr_data <= 8'd0;
	else if(ram_addr == 6'd31)
		ram_wr_data <= 8'd0;
	else
		ram_wr_data <= ram_wr_data + 1'b1;
end

endmodule

4)编写顶层文件

module top_singlep_ram_test(
	input clk,
	input rst_n
    );
wire 		ram_wea;        
wire 		ram_en;        
wire [4:0]	ram_addr;
wire [7:0]	ram_wr_data;
wire [7:0]	ram_rd_data;  

// ram_rw 读写模块
ram_rw u_ram_rw(
	.clk             (clk),
	.rst_n           (rst_n),
                     
	.ram_wea         (ram_wea),
	.ram_en          (ram_en),
	.ram_addr        (ram_addr),
	.ram_wr_data     (ram_wr_data),
	.ram_rd_data     (ram_rd_data)
);

// ip_ram 核
blk_mem_gen_0 u_blk_mem_gen_0 (
	.clka		(clk),    // input wire clka
	.ena		(ram_en),      // input wire ena
	.wea		(ram_wea),      // input wire [0 : 0] wea
	.addra		(ram_addr),  // input wire [4 : 0] addra
	.dina		(ram_wr_data),    // input wire [7 : 0] dina
	.douta		(ram_rd_data)  // output wire [7 : 0] douta
);

endmodule

5)编写激励文件

`timescale 1ns / 1ps

module tb_ip_ram();

reg		clk;
reg 	rst_n;

initial begin
	clk = 1'b0;
	rst_n = 1'b0;
	#200
	rst_n = 1'b1;
end

always #10 clk = ~clk;

top_singlep_ram_test u_top_singlep_ram_test(
	.clk    (clk),
	.rst_n  (rst_n)
);
endmodule

6)仿真测试

IP 核之RAM实验_第6张图片
由上图可知,ram_wea 信号拉高,说明此时是对 ram 进行写操作。ram_wea 信号拉高之后,地址和数据都是从 0 开始累加,也就说当 ram 地址为 0 时,写入的数据也是 0;当 ram 地址为 1 时,写入的数据也是 1,我们总共向 ram 中写入 32 个数据。
IP 核之RAM实验_第7张图片
由上图可知,ram_wea 信号拉低,说明此时是对 ram 进行读操作。ram_wea 信号拉低之后,ram_addr从 0 开始增加,也就是说从 ram 的地址 0 开始读数据;ram 中读出的数据 ram_rd_data 在延时一个时钟周期之后,开始输出数据,输出的数据为 0,1,2……,和我们写入的值是相等的。

7)ILA测试

添加 ILA IP 核,将 ram_en、ram_wea、ram_addr、ram_wr_data 和 ram_rd_data 信号添加至观察列表中。

IP 核之RAM实验_第8张图片图1 PICTURE ONE
IP 核之RAM实验_第9张图片图2 PICTURE TWO

ILA 的时钟和探针信号连接到顶层设计中,例化 ILA IP 核的代码如下:

ila_0 your_instance_name (
	.clk(clk), // input wire clk

	.probe0(ram_en), // input wire [0:0]  probe0  
	.probe1(ram_wea), // input wire [0:0]  probe1 
	.probe2(ram_addr), // input wire [4:0]  probe2 
	.probe3(ram_wr_data), // input wire [7:0]  probe3 
	.probe4(ram_rd_data) // input wire [7:0]  probe4
);

最后为工程添加 IO 管脚约束,对应的 XDC 约束语句如下所示:

set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports clk]
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports rst_n]

生成比特流之后,直接点击“Program”,此时 Vivado 会自动打开 ILA 的调试窗口,如下图所示:
IP 核之RAM实验_第10张图片
IP 核之RAM实验_第11张图片

3.实验2: 配置伪双端口 RAM

1)创建工程并添加ram ip

IP 核之RAM实验_第12张图片

IP 核之RAM实验_第13张图片图1 PICTURE ONE
IP 核之RAM实验_第14张图片图2 PICTURE TWO
IP 核之RAM实验_第15张图片图3 PICTURE ONE
IP 核之RAM实验_第16张图片图4 PICTURE TWO

注意:在Port B Options 栏目下,Primitives Output Register 取消勾选,其功能是在输出数据加上寄存器,可以有效改善时序,但读出的数据会落后地址两个周期。很多情况下,不使能这项功能,保持数据落后地址一个周期。

完成配置后,点击“Generate”生成 RAM IP。

2)伪双端口RAM的端口定义和时序

IP 核之RAM实验_第17张图片

Simple Dual Port RAM 模块端口的说明如下:

信号名称 方向 说明
clka in 端口A时钟输入
wea in 端口A使能
addra in 端口A地址输入
dina in 端口A数据输入
clkb in 端口B时钟输入
addrb in 端口B地址输入
doutb in 端口B数据输出
IP 核之RAM实验_第18张图片图1 写时序
IP 核之RAM实验_第19张图片图2 读时序

RAM 的数据写入和读出都是按时钟的上升沿操作的,端口 A 数据写入的时候需要置高wea 信号,同时提供地址和要写入的数据。

而端口 B 是不能写入数据的,只能从 RAM 中读出数据,只要提供地址就可以了,一般情况下可以在下一个周期采集到有效的数据。

3)编写测试程序

/*
 * 当计数范围在 0~511 之间时,向 ram 中写入数据;
 * 当计数范围在 512~1023 之间时,从 ram 中读出数据。
 */

module ip_simple_port_ram_test(
	input	clk,
	input	rst_n
    );
    
wire ram_wea;
reg		[8:0] 	ram_wr_addr; 
reg		[15:0]	ram_wr_data;
reg		[8:0] 	ram_rd_addr; 
wire	[15:0]	ram_rd_data;

reg [9:0] cnt; 

//读写使能信号
assign ram_wea = (cnt < 10'd512) ? 1'b1:1'b0; 

//计数器 范围(0~1023)
always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
		cnt <= 1'b0;
	else if(cnt == 10'd1023)
		cnt <= 1'b0;
	else
		cnt <= cnt + 1'b1;			
end

//写信号地址及数据
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		ram_wr_addr <= 1'b0;
		ram_wr_data <= 1'b0;
	end
	else if(ram_wr_addr == 9'd511 && ram_wr_data == 16'd511) begin
		ram_wr_addr <= 1'b0;
		ram_wr_data <= 1'b0;	
	end
	else begin
		ram_wr_addr <= ram_wr_addr + 1'b1;
		ram_wr_data <= ram_wr_data + 1'b1;
	end
end

//读信号地址
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) 
		ram_rd_addr <= 1'b0;
	else if (ram_rd_addr == 9'd511)
		ram_rd_addr <= 1'b0;
	else
		ram_rd_addr <= ram_rd_addr + 1'b1;
end

//例化 ip ram核
ip_simple_ram u_ip_simple_ram (
  .clka(clk),    // input wire clka
  .wea(ram_wea),      // input wire [0 : 0] wea
  .addra(ram_wr_addr),  // input wire [8 : 0] addra
  .dina(ram_wr_data),    // input wire [15 : 0] dina
  .clkb(clk),    // input wire clkb
  .addrb(ram_rd_addr),  // input wire [8 : 0] addrb
  .doutb(ram_rd_data)  // output wire [15 : 0] doutb
);

endmodule

4)编写激励文件

`timescale 1ns / 1ps

module tb_ip_simport_ram();

reg		clk;
reg 	rst_n;

initial begin
	clk = 1'b0;
	rst_n = 1'b0;
	#200
	rst_n = 1'b1;
end

always #10 clk = ~clk;

ip_simple_port_ram_test u_ip_simple_port_ram_test(
	.clk    (clk),
	.rst_n  (rst_n)
);


endmodule

5)仿真测试

IP 核之RAM实验_第20张图片IP 核之RAM实验_第21张图片

6)ILA测试

添加 ILA IP 核,将 ram_wea、ram_wr_addr、ram_wr_data 、ram_rd_addr和ram_rd_data 信号添加至观察列表中。

IP 核之RAM实验_第22张图片图1 PICTURE ONE
IP 核之RAM实验_第23张图片图2 PICTURE TWO

ILA 的时钟和探针信号连接到顶层设计中,例化 ILA IP 核的代码如下:

//例化 ILA IP核
ila_0 your_instance_name (
	.clk(clk), // input wire clk

	.probe0(ram_wea), // input wire [0:0]  probe0  
	.probe1(ram_wr_addr), // input wire [8:0]  probe1 
	.probe2(ram_wr_data), // input wire [15:0]  probe2 
	.probe3(ram_rd_addr), // input wire [8:0]  probe3 
	.probe4(ram_rd_data) // input wire [15:0]  probe4
);

最后为工程添加 IO 管脚约束,对应的 XDC 约束语句如下所示:

set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]

生成比特流之后,直接点击“Program”,此时 Vivado 会自动打开 ILA 的调试窗口,如下图所示:

IP 核之RAM实验_第24张图片
IP 核之RAM实验_第25张图片

你可能感兴趣的:(FPGA,fpga,verilog)