ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

FPGA--RAM

FPGA--RAM

一、基本介绍

这一部分我在csdn找到了一篇计算机组成的文章,大家可以通过这个通俗易懂的文章进行学习

计算机组成与体系结构:RAM(随机存取存储器)

RAM:

单端口:只有一个端口,读写不能同时进行,共用数据通道

将单端口的写功能关闭,只用读端口,可以当作room使用,具体后续文章会补充

伪双端口(SDP):有两个端口,一个用来读,一个用来写,分工明确

真双端口:有两个端口,各自都能读或者写

二、实验部分(ip核配置)

1基本介绍

我们实验部分主要是用一个ram,0-31存数据,然后32-63将数据读出

2ip核配置(1)

创建好.v文件,点击IP catlog,搜索ram,选择block memory generator

对于第一个,interface type可以选native或者AXI4,我们选native(普通版)就行

对于第二个,memory type,我们可以选的类型就比较多了

从上到下依次是,单端口,伪双端口,真双端口,单端ROM,双端ROM

其中ROM就是把我们的RAM的写功能禁止了,只能用来读数据

第三个,ECC Options

这实现的是纠错功能(但是对于我们的单端口,是无法选中这个功能的),只有伪双端口才有这个纠错功能

第四个,write enable(写使能)

勾选之后,按照一字节一字节进行写数据(数据只能是8位或者9位的整数倍)

第五个,algorithm options

这一部分选择有三个,从上到下依次是最小面积,最低功耗,固定原语

primitive是他的默认容量,我这边是8kx2

3ip核配置(2)

首先memory width配置,我们需要配置写位宽,读位宽,写的数据量,读的数据量

operating mode里面用来选择我们是写优先/读优先

enable port type:使能功能,可选always enable(一直使能)或者use ENA pin(给一个信号来控制使能)

port A optional output register:输出寄存器,如果勾选之后,RAM的输出数据会在这个寄存器里面缓冲,然后释放数据,使得数据有延迟

但是这不意味着我们就一定不用勾选他,勾选不勾选都有各自的利弊

不勾选:造成时序的不严谨,造成错误的数据

port A output reset options:复位

接下来简单介绍一下现在的各个引脚:

从上到下依次是,地址位,时钟,数据输入,数据输出,使能,读写切换

4ip核配置(3)

这一部分我们用作RAM的直接默认即可,如果后续我们采用这个ip核配置ROM的话,我们需要在这里加载一个coe文件

三、IP核例化使用

1、例化ip核

首先,找到我们的例化模板,注意是veo文件

2、编写读写模块

我们需要一个读写模块来驱动这个ram的ip核,新建一个v文件,命名为ram_rw

这部分的数据和地址都是由计数器提供的

module ram_rw( input clk, input rst_n, //ram ip核需要一个使能信号,定义成reg型,后续用always进行赋值 output reg ram_en, //ram ip核需要读写切换,定义成reg型 output reg rw, //ram ip核需要地址信号,5位二进制数则是2^5=32位,定义成reg型 output reg [4:0] ram_addr, //ran ip核写数据,数据位宽8位 output reg [7:0] ram_wr_data, ); endmodule //定义一个计数器判断什么时候该读数据,什么时候写数据,由于我们题目说0-31写数据,32-63读数据,64个数字需要6位数据位宽 reg [5:0] rw_cnt; //ram ip核使能部分 always@(posedge clk or negedge rst_n)begin if(!rst_n) ram_en<=1'b0; else ram_en<=1'b1; end //1、读写计数器逻辑 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 //2、读写切换逻辑 always@(posedge clk or negedge rst_n)begin if(!rst_n) rw<=1'b1;//默认写状态 else if(rw_cnt<6'd31) rw<=1'b1;//写状态,由于读写数据处理考虑使能,这里可以不用考虑,直接切换读写状态 else rw<=1'b0;//读状态 end //3、读写数据处理 always@(posedge clk or negedge rst_n)begin if(!rst_n) ram_wr_data<=8'd0; //写数据逻辑,注意需要使能 else if((rw_cnt<=6'd31)&&ram_en) ram_wr_data<=ram_wr_data+8'd1;//由于题目中数据也是0-31,所以直接类似计数器即可 else ram_wr_data<=ram_wr_data;//如果未使能/不在写数据阶段,就保存数据 end //4、读写地址处理 always@(posedge clk or negedge rst_n)begin if(!rst_n) ram_addr<=5'd0; else ram_addr<=rw_cnt[4:0];//地址同样由计数器决定,为了不超位数,只取计数器的低5位 end

四、编写代码

1、添加读写模块进入主文件

将ram和读写模块例化

`timescale 1ns / 1ps module ip_ram( input sys_clk, input sys_rst_n ); wire ram_en; wire rw; wire [4:0] ram_addr; wire [7:0] ram_wr_data; wire [7:0] douta; ram_rw ram_rw_inst( .clk(sys_clk), .rst_n(sys_rst_n), .ram_en(ram_en), .rw(rw), .ram_addr(ram_addr), .ram_wr_data(ram_wr_data) ); blk_mem_gen_0 ram_inst ( .clka(sys_clk), // input wire clka .ena(ram_en), // input wire ena .wea(rw), // input wire [0 : 0] wea .addra(ram_addr), // input wire [4 : 0] addra .dina(ram_wr_data), // input wire [7 : 0] dina .douta(douta) // output wire [7 : 0] douta ); endmodule

2、对引脚进行配置

点击右上方的I/O planing

注意检查一下约束文件对于时钟是否已经配置好50mhz了,我是经常配置出问题

# 时钟物理约束 set_property PACKAGE_PIN N18 [get_ports sys_clk] set_property IOSTANDARD LVCMOS33 [get_ports sys_clk] # 复位物理约束 set_property PACKAGE_PIN D19 [get_ports sys_rst_n] set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_n] # 关键:时钟时序约束(50MHz -> 20ns 周期) create_clock -period 20.000 -name sys_clk [get_ports sys_clk]

3、添加ila信号

在顶层模块连接好

`timescale 1ns / 1ps module ip_ram( input sys_clk, input sys_rst_n ); wire ram_en; wire rw; wire [4:0] ram_addr; wire [7:0] ram_wr_data; wire [7:0] douta; ram_rw ram_rw_inst( .clk(sys_clk), .rst_n(sys_rst_n), .ram_en(ram_en), .rw(rw), .ram_addr(ram_addr), .ram_wr_data(ram_wr_data) ); blk_mem_gen_0 ram_inst ( .clka(sys_clk), // input wire clka .ena(ram_en), // input wire ena .wea(rw), // input wire [0 : 0] wea .addra(ram_addr), // input wire [4 : 0] addra .dina(ram_wr_data), // input wire [7 : 0] dina .douta(douta) // output wire [7 : 0] douta ); ila_0 your_instance_name ( .clk(sys_clk), // input wire clk .probe0(ram_en), // input wire [0:0] probe0 .probe1(rw), // input wire [0:0] probe1 .probe2(ram_addr), // input wire [4:0] probe2 .probe3(ram_wr_data), // input wire [7:0] probe3 .probe4(douta) // input wire [7:0] probe4 ); endmodule
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进