概述
简单总结ISE中RAM的ip核配置过程以及相关的端口。
分类
ram分为分布式ram(distributed ram)以及块ram(block ram)
前者是自己用寄存器搭建的,这里理解可以转至Vivado使用技巧(27):RAM编写技巧
简单理解块ram就是自己用寄存器写出来的储存单元,仅仅用于储存比较少量的数据。优点是dram不需要时钟线来控制,可以直接用组合逻辑进行控制。bram是fpga中定制的ram资源,控制起来需要用时钟线,以及其他UI界面设定的io接口进行配置。
定制过程及说明
- 首先进入页面,打开进行选择要生成的ip核
- 选择你要定制的类型
可以选择的类型有如下五个,其中两种是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)
- 数据端口配置
这里可以配置写入的宽度和深度。
同时可以尽行不同宽度的读取。就是说写入的宽度不同于读取的宽度。系统会对深度进行自动设定。
当选择真正双口ram的实时候,会出现这里的三个设计端口。
- 其他配置
后面还有几页,这里笔者能力有限,以后进行补充。
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编写技巧
感谢~
最后
以上就是爱撒娇服饰为你收集整理的ISE使用中RAM IP核配置及ram测试(两种测试)分类定制过程及说明ram测试(1)ram测试(2)Aram测试(2)B的全部内容,希望文章能够帮你解决ISE使用中RAM IP核配置及ram测试(两种测试)分类定制过程及说明ram测试(1)ram测试(2)Aram测试(2)B所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复