原创声明:
本原创教程由芯驿电子科技(上海)有限公司(ALINX)创作,版权归本公司所有,如需转载,需授权并注明出处。
适用于板卡型号:
AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4EV-P/AXU5EV-E/AXU5EV-P /AXU9EG/AXU15EG
实验Vivado工程为“ram_test”。
RAM是FPGA中常用的基础模块,可广泛用于缓存数据的情况,同样它也是ROM,FIFO的基础。本实验将为大家介绍如何使用FPGA内部的RAM以及程序对该RAM的数据读写操作。
1.实验原理
Xilinx在VIVADO里为我们已经提供了RAM的IP核, 我们只需通过IP核例化一个RAM,根据RAM的读写时序来写入和读取RAM中存储的数据。实验中会通过VIVADO集成的在线逻辑分析仪ila,我们可以观察RAM的读写时序和从RAM中读取的数据。
2.创建Vivado工程
在添加RAM IP之前先新建一个ram_test的工程, 然后在工程中添加RAM IP,方法如下:
2.1 点击下图中IP Catalog,在右侧弹出的界面中搜索ram,找到Block Memory Generator,双击打开。
2.2 将Component Name改为ram_ip,在Basic栏目下,将Memory Type改为Simple Dual Prot RAM,也就是伪双口RAM。一般来讲"Simple Dual Port RAM"是最常用的,因为它是两个端口,输入和输出信号独立。
2.3 切换到Port A Options栏目下,将RAM位宽Port A Width改为16,也就是数据宽度。将RAM深度Port A Depth改为512,深度指的是RAM里可以存放多少个数据。使能管脚Enable Port Type改为Always Enable。
2.4 切换到Port B Options栏目下,将RAM位宽Port B Width改为16,使能管脚Enable Port Type改为Always Enable,当然也可以Use ENB Pin,相当于读使能信号。而Primitives Output Register取消勾选,其功能是在输出数据加上寄存器,可以有效改善时序,但读出的数据会落后地址两个周期。很多情况下,不使能这项功能,保持数据落后地址一个周期。
2.5 在Other Options栏目中,这里不像ROM那样需要初始化RAM的数据,我们可以在程序中写入,所以配置默认即可,直接点击OK。
2.6 点击“Generate”生成RAM IP。
3. RAM的端口定义和时序
Simple Dual Port RAM 模块端口的说明如下:
信号名称 | 方向 | 说明 |
clka | in | 端口A时钟输入 |
wea | in | 端口A使能 |
addra | in | 端口A地址输入 |
dina | in | 端口A数据输入 |
clkb | in | 端口B时钟输入 |
addrb | in | 端口B地址输入 |
doutb | out | 端口B数据输输出 |
RAM的数据写入和读出都是按时钟的上升沿操作的,端口A数据写入的时候需要置高wea信号,同时提供地址和要写入的数据。下图为输入写入到RAM的时序图。
RAM写时序
而端口B是不能写入数据的,只能从RAM中读出数据,只要提供地址就可以了,一般情况下可以在下一个周期采集到有效的数据。
RAM读时序
4. 测试程序编写
下面进行RAM的测试程序的编写,由于测试RAM的功能,我们向RAM的端口A写入一串连续的数据,只写一次,并从端口B中读出,使用逻辑分析仪查看数据。代码如下
`timescale1ns/1ps
//////////////////////////////////////////////////////////////////////////////////
module ram_test(
input clk, //25MHz时钟
input rst_n //复位信号,低电平有效
);
//-----------------------------------------------------------
reg [8:0] w_addr; //RAM PORTA写地址
reg [15:0] w_data; //RAM PORTA写数据
reg wea; //RAM PORTA使能
reg [8:0] r_addr; //RAM PORTB读地址
wire [15:0] r_data; //RAM PORTB读数据
//产生RAM PORTB读地址
always@(posedge clk ornegedge rst_n)
begin
if(!rst_n)
r_addr <=9'd0;
elseif(|w_addr) //w_addr位或,不等于0
r_addr <= r_addr+1'b1;
else
r_addr <=9'd0;
end
//产生RAM PORTA写使能信号
always@(posedge clk ornegedge rst_n)
begin
if(!rst_n)
wea <=#11'b0;
else
begin
if(&w_addr)//w_addr的bit位全为1,共写入512个数据,写入完成
wea <=#11'b0;
else
wea <=#11'b1;//ram写使能
end
end
//产生RAM PORTA写入的地址及数据
always@(posedge clk ornegedge rst_n)
begin
if(!rst_n)
begin
w_addr <=9'd0;
w_data <=16'd1;
end
else
begin
if(wea) //ram写使能有效
begin
if(&w_addr)//w_addr的bit位全为1,共写入512个数据,写入完成
begin
w_addr <= w_addr ;//将地址和数据的值保持住,只写一次RAM
w_data <= w_data ;
end
else
begin
w_addr <= w_addr +1'b1;
w_data <= w_data +1'b1;
end
end
end
end
//-----------------------------------------------------------
//实例化RAM
ram_ip ram_ip_inst (
.clka (clk ),// input clka
.wea (