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
相关推荐
FPGA小迷弟7 小时前
FPGA 与千兆以太网校验和:从原理到 Verilog 实现
嵌入式硬件·fpga开发·无人机·ai编程·fpga
码农小韩1 天前
FPGA学习(二)——LED闪烁
fpga开发·verilog·fpga·led·组合逻辑·分频设计
9527华安2 天前
FPGA纯verilog实现DP视频接口,支持2K60帧分辨率,提供3套工程源码和技术支持
fpga开发·音视频·dp
ChaseVLSI2 天前
FPGA与ASIC的区别详解:内部结构、开发成本与适用场景,项目选型到底该选哪个
嵌入式硬件·fpga开发·硬件架构·硬件工程
9527华安3 天前
FPGA实现ARINC818总线协议,基于GT Transceivers Wizard高速收发器,提供7套工程源码和技术支持
fpga开发·transceivers·arinc818
小蘇的FPGA3 天前
fpga-wide-data-crc32
fpga开发
落雨无风4 天前
fpga板级调试常见问题
fpga开发
码农小韩4 天前
FPGA学习(一)——LED点灯与开发流程
学习·fpga开发·入门·环境配置·仿真·点灯
FPGA小迷弟4 天前
FPGA 与千兆以太网:各类物理接口的形式、区别、使用方法与应用场景详解
tcp/ip·macos·fpga开发·udp·fpga
NamoAmitabha1284 天前
Zynq 的 AXI GPIO,是“ARM 通过一条国际标准高速公路(AXI 总线),去遥控 FPGA 里的一个寄存器”。---deepseek
fpga开发·zynq--ps