Xilinx系FPGA学习笔记(九)DDR3学习

系列文章目录


文章目录


前言

这里暂时先只介绍一下IP核配置生成和一些接口信号的含义,后续还需要补很多知识点和实际测试应用

DDR介绍

DDR3 已不是当今主流的 DDR 存储器,市场上的 DDR4和 DDR5 也已经应用广泛。但是 DDR3 存储器作为 RAM 存储器家族发展历程中的一个重要里程碑。

最早是SRAM,由六个晶体管构成,实现1位数据的存储

早期 SRAM 芯片对于晶体管的消耗量是巨大的,影响芯片的成本最大的一个因素就是芯片内部集成的晶体管数量,所以后续进行研发实现了是使用 1 个电容+1 个晶体管的组合,实现 1 位数据的存储。

SDRAM:

如果给电容两端施加电压,电容两端就会形成一个电势差。如果电容里面储存有电荷,那么这两端就会有电压差,此时,我们就可以认为值存储为 1;如果电荷电容器里面没有电压差,则说明该电容内没有储存电荷,那么这个时候我们就认为它存储的数据为 0。

现如今,SDRAM 仍然在当前部分缓存容量和缓存数据要求不高的场合,凭借其成本优势具有一定实用价值。

SDRAM 最大的特点就是容量大,因为用到的晶体管少,但是工作频率并没有明显提高,所以就出现了DDR SDRAM

DDR SDRAM 的核心技术在于存储单元的读写速率和存储器接口的读写速率分离。

SDRAM 存储单元的读写速率和存储接口的读写速率近乎相同,且存储单元的读写速率遇到瓶颈,很难再有提升空间。所以之后研制了让存储器接口在同一个存储单元的存储周期读写 2bit 的数据,这样速度就加快了一倍,就是 DDR SDRAM

另外可以在时钟的上升沿和下降沿都进行数据的交互,就是我们常说的双数据率(Double Data Rate)

DDR2只是对存储器的接口速率进行了一些优化,以 200M(400M 双沿等效)时钟传输数据,同时采用了 4bit 预读取技术

DDR3 口以 400M(800M 双沿等效)时钟传输数据,同时采用了 8bit 预读取技术。所以在200M的周期内,可以读写32bit,这里优化不仅是提高时钟频率,涉及到了时序约束,让器件工作在其规定的时序内。

RAM的发展历史如下:

DDR的IP核学习

我买的小梅哥开发板ACX750设计有两片型号为 MT41K256M16 RE-125 的 DDR 3存储器件,每片位宽为 16 位,因此并联组成为位宽 32 位,存储深度 256M 的存储器件。

下面先学习两片并联DDR3的使用方法

DDR 控制器的名称简写为 MIG(Memory Interface Generator)

勾选"Create Design",默认名称(Component Name)为"mig_7series_0",用户可对其进行修改,这里保持默认。选择控制器数量(Number of Controllers)为 1,勾选 AXI4 Interface

兼容性设计:选择了其他型号的FPGA,具有相同封装的不同FPGA器件,MIG将仅选择目标设备和所有选定设备之间通用的引脚。为目标零件使用par文件夹中的默认XDC。如果目标部件已更改,请使用compatible_ucf文件夹中的相应XDC。如果现在不选择引脚兼容FPGA,以后使用不同的FPGA,则生成的XDC可能不适用于新设备。

下面进行DDR3的SDRAM的配置:

主要设置有:

1、DDR3 存储器驱动的时钟周期(Clock Period)设置为 2500ps(即 400MHz), 这个时钟是用于 FPGA 输出给到 DDR 存储器时钟管脚的时钟。注意这里根据实际情况是有设置 区 间 范 围 的 ,并非任意值 ,这里的区间范围为2500~3300ps( 即300 ~400MHz)。

2、 DDR3 存储器类型(Memory Type)为 Components。

3、DDR3 存储器型号(Memory Part)为 MT41K256M16XX-125,这是开发板板载 DDR3存储器的实际型号(XX 表示任何字符均可)。此处倒三角点击后有很多备选型号,若实际使用型号不在此列表中,可以点击"Create Custom Part"后设置相关 DDR3 存储器的时序参数。

4、DDR3 存储器接口电压(Memory Voltage)为 1.5V。

5、 DDR3 存储器位宽(Data Width),对于 ACX750 而言, 使用单片 DDR 则每个地址对应 16 位数据;使用双片 DDR 则每个地址对应 32 位数据。但是,对于用户端来说,mig 会以字节为单位进行读写,因此,用户端每个地址对应 8 位数据。

6、 DDR 控制器的 bank machines 个数设置,这里参数与 DDR3 物理 bank 个数并非是同一概念,设置上并非一定需要与 DDR3 物理 bank 个数保持一致(当然设置相同数量可以增加 DDR 控制器的效率和性能,但是会占用相对多的资源,时序上要求也相对要高,性能和资源上如何达到一个比较好的平衡,需要根据实际应用场景进行设置。

7、DDR 控制器调度命令的顺序的配置,当选择 strict 时,严格按照命令先后顺序执行;选择 normal 时,为了得到更高的效率,可能对命令重排序。为了操作简单,我们选择strict

之后会进入到AXI接口的相关参数的配置

1、AXI 接口的数据位宽,设置为 128。

2、DDR 控制器的仲裁机制,由于 AXI 接口读写通道是独立的,读写各有自己的地址通道,而储存器控制器只有一个地址总线,同一时刻只能进行读或写,这样就存在读/写优先级的问题,这里设置 TDM(Time Division Multiplexing),该设置读写优先级相同,读写交替进行。

3、Narrow Burst 支持,设置 0,将其关闭。

4、AXI 接口的地址位宽,自动根据 DDR3 内存生成的位宽,这里 AXI 地址对应的数据是以 1 字节进行计算的,不要与 DDR3 的地址和存储数据混淆。板载 DDR3 存储器存储空间 8Gbit(216 256=8Gbit = 1GByte = 2^30 Byte,所以 AXI 的地址位宽为 30)。

5、 AXI 读/写通道的 ID 宽度。 ID 是用来标识读/写响应和读/写数据的一致性,具体后面

在讲解 AXI 协议会讲。

之后对Memory Options进行配置

1、输入系统时钟频率设置, 这个时钟是提供给 MIG IP 的时钟,没有特别要求。如下图所示为 MIG IP 内部关于时钟的结构图,可以看到 MIG IP 里面有一个时钟锁相环,系统时钟是这个时钟锁相环的输入时钟,锁相环会根据这里的输入时钟自动产生 MIG IP 内部各种所需的时钟。下拉框中的各种频率值都可以选择。 MIG IP 输入还需要一个 200M 的 IDELAY Reference Clock 时钟,为了将两个时钟共用一个输入时钟,将这里的系统时钟周期(Input Clock Period) 配置选择为 5000ps(200MHz)。

2、突发读类型和长度(Read Burst Type and Length)设置为顺序读写 Sequential。

3、输出驱动阻抗控制(Output Drive Impedance Control)选择 R ZQ/7。

4、片上终端(On Die Termination)设置为 R ZQ/4

5、 片选信号(Controller Chip Select Pin)设置为 Enable,即使用该引脚,实际开发板的DDR3 的 CS 信号有连接到 FPGA 管脚,所以这里需要使用该引脚。如果硬件上 DDR3管脚未连接到 FPGA,那么这里就可以设置为 Disable。

6、 DDR 和 AXI 总线之间的地址映射存储器地址映射选择(Memory Address MappingSelection)。默认选择后者

之后进入FPGA Option的配置界面:

1、系统时钟(System Clock): 这里的系统 200M 时钟由 FPGA 内部提供,不由管脚输入,选择 No Buffer,如果实际硬件管脚有提供 200MHz 时钟,也可以选择 Differential(差分输入)或 Signal-Ended(单端输入)。

2、 参考时钟(Reference Clock):该时钟需要频率为 200MHz 时钟,由于在前面配置中将系统时钟设置为 200MHz,所以可以选择 Use System Clock,可以将两个输入时钟合并一个共用的输入。如果前面的系统时钟设置的不是 200MHz 这里配置选项就没有"Use System Clock"可选,只能由管脚端口输入时钟或者FPGA 内部产生这个 200MHz 时钟。(这个必须是200M的时钟)

3、系统复位极性(System Reset Polarity): 选择 ACTIVE LOW。

4、存储器控制器的调试信号(Debug Signal for Memory Controller)选择 OFF。

5、 勾选 internal Verf。

其他保留默认设置。

点击 Next 进入到如下图所示的 Extended FPGA Option 配置页面中,设定内部终端阻抗(Internal Termination Impedance)为 50 Ohm。

点击 Next 进入到如下图所示的 IO Planning Option 配置页面中,仅仅仿真,可以先选择 New Design。进行上板测试,则选择"Fixed Pin Out: Preexisting pin out is known and fixed"。

所有 DDR3 存储器相关的引脚定义引脚号(Pin Number)以及 IO 电平标准(IO Standard) 的配置,需要和原理图连接相一致。

这里有两种配置方式,其中一种配置方式是采用手动输入的方式,该种方式下只需要对配置界面中 Pin Number 那一列根据原理图或提供的管脚分配表选择相应的管脚即可。在选择设置完后, Bank Number 和 Byte Number 会自动填充对应的配置。手动输入模式下,IO Standard 不需要手动输入配置。

还可以导入.ucf 约束文件方式,提前写好,或者用别人写的文件改一改,比手动一个一个配置要快捷

validate检查没问题之后就可以下一步了,进入到如下图所示的 Simulation Options 页面, 勾选"Accept"。
)

进入到 PCB Information 页面, 无需设置,之后直接生成IP核就行了

接口信号解析

下面是生成的所有接口信号

c 复制代码
mig_7series_0 u_mig_7series_0 (
// Memory interface ports
.ddr3_addr (ddr3_addr), // output [14:0] ddr3_addr
.ddr3_ba (ddr3_ba), // output [2:0] ddr3_ba
.ddr3_cas_n (ddr3_cas_n), // output ddr3_cas_n
.ddr3_ck_n (ddr3_ck_n), // output [0:0] ddr3_ck_n
.ddr3_ck_p (ddr3_ck_p), // output [0:0] ddr3_ck_p
.ddr3_cke (ddr3_cke), // output [0:0] ddr3_cke
.ddr3_ras_n (ddr3_ras_n), // output ddr3_ras_n
.ddr3_reset_n (ddr3_reset_n), // output ddr3_reset_n
.ddr3_we_n (ddr3_we_n), // output ddr3_we_n
.ddr3_dq (ddr3_dq), // inout [31:0] ddr3_dq
.ddr3_dqs_n (ddr3_dqs_n), // inout [3:0] ddr3_dqs_n
.ddr3_dqs_p (ddr3_dqs_p), // inout [3:0] ddr3_dqs_p
.init_calib_complete (init_calib_complete), //
output init_calib_complete
.ddr3_cs_n (ddr3_cs_n), // output [0:0] ddr3_cs_n
.ddr3_dm (ddr3_dm), // output [3:0] ddr3_dm
.ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt
// Application interface ports
.ui_clk (ui_clk), // output ui_clk
.ui_clk_sync_rst (ui_clk_sync_rst), //
output ui_clk_sync_rst
.mmcm_locked (mmcm_locked), // output mmcm_locked
.aresetn (aresetn), // input aresetn
.app_sr_req (app_sr_req), // input app_sr_req
.app_ref_req (app_ref_req), // input app_ref_req
.app_zq_req (app_zq_req), // input app_zq_req
.app_sr_active (app_sr_active), // output app_sr_active
.app_ref_ack (app_ref_ack), // output app_ref_ack
.app_zq_ack (app_zq_ack), // output app_zq_ack
// Slave Interface Write Address Ports
.s_axi_awid (s_axi_awid), // input [3:0] s_axi_awid
.s_axi_awaddr (s_axi_awaddr), // input [29:0] s_axi_awaddr
.s_axi_awlen (s_axi_awlen), // input [7:0] s_axi_awlen
.s_axi_awsize (s_axi_awsize), // input [2:0] s_axi_awsize
.s_axi_awburst (s_axi_awburst), // input [1:0] s_axi_awburst
.s_axi_awlock (s_axi_awlock), // input [0:0] s_axi_awlock
.s_axi_awcache (s_axi_awcache), // input [3:0] s_axi_awcache
.s_axi_awprot (s_axi_awprot), // input [2:0] s_axi_awprot
.s_axi_awqos (s_axi_awqos), // input [3:0] s_axi_awqos
.s_axi_awvalid (s_axi_awvalid), // input s_axi_awvalid
.s_axi_awready (s_axi_awready), // output s_axi_awready
// Slave Interface Write Data Ports
.s_axi_wdata (s_axi_wdata), // input [127:0] s_axi_wdata
.s_axi_wstrb (s_axi_wstrb), // input [15:0] s_axi_wstrb
.s_axi_wlast (s_axi_wlast), // input s_axi_wlast
.s_axi_wvalid (s_axi_wvalid), // input s_axi_wvalid
.s_axi_wready (s_axi_wready), // output s_axi_wready
// Slave Interface Write Response Ports
.s_axi_bid (s_axi_bid), // output [3:0] s_axi_bid
.s_axi_bresp (s_axi_bresp), // output [1:0] s_axi_bresp
.s_axi_bvalid (s_axi_bvalid), // output s_axi_bvalid
.s_axi_bready (s_axi_bready), // input s_axi_bready
// Slave Interface Read Address Ports
.s_axi_arid (s_axi_arid), // input [3:0] s_axi_arid
.s_axi_araddr (s_axi_araddr), // input [29:0] s_axi_araddr
.s_axi_arlen (s_axi_arlen), // input [7:0] s_axi_arlen
.s_axi_arsize (s_axi_arsize), // input [2:0] s_axi_arsize
.s_axi_arburst (s_axi_arburst), // input [1:0] s_axi_arburst
.s_axi_arlock (s_axi_arlock), // input [0:0] s_axi_arlock
.s_axi_arcache (s_axi_arcache), // input [3:0] s_axi_arcache
.s_axi_arprot (s_axi_arprot), // input [2:0] s_axi_arprot
.s_axi_arqos (s_axi_arqos), // input [3:0] s_axi_arqos
.s_axi_arvalid (s_axi_arvalid), // input s_axi_arvalid
.s_axi_arready (s_axi_arready), // output s_axi_arready
// Slave Interface Read Data Ports
.s_axi_rid (s_axi_rid), // output [3:0] s_axi_rid
.s_axi_rdata (s_axi_rdata), // output [127:0] s_axi_rdata
.s_axi_rresp (s_axi_rresp), // output [1:0] s_axi_rresp
.s_axi_rlast (s_axi_rlast), // output s_axi_rlast
.s_axi_rvalid (s_axi_rvalid), // output s_axi_rvalid
.s_axi_rready (s_axi_rready), // input s_axi_rready
// System Clock Ports
.sys_clk_i (sys_clk_i),
.sys_rst (sys_rst) // input sys_rst
);

按顺序解读:

1、带 ddr3 的信号是与外部 DDR3 存储器的接口;

2、信号 init_calib_complete 是 DDR 控制器对外部 DDR3 存储器初始化和校准完成信号,若该信号为高,表示 DDR 初始化和校准完成,之后用户可往 DDR 进行数据的读写操作了。

3、带 app 的信号是本地接口维护命令信号,这几个信号可以不用使用,输入信号直接给 0,输出信号不连接其他信号。

app_sr_req: 输入,保留信号,接低电平。

app_sr_active:输出 ,保留信号。

app_ref_req:输入。置位时,高电平请求内存控制器向 DRAM 发送刷新命令。必须在一个周期内进行脉冲以发出请求,直到 app_ref_ack 信号被置位以确认请求并指示它已被发送,然后置为无效。

app_ref_ack:输出。置位时,高电平确认刷新请求,并指示该命令已从存储器控制器发送到外部 DDR。

app_zq_req :输入。置位时,高电平有效输入请求存储器控制器向 DRAM 发送 ZQ 校准命令。必须在一个周期内进行脉冲以发出请求,直到 app_zq_ack 信号被置位以确认请求并指示它已被发送,然后取消置位。

app_zq_ack:输出。置位时,高电平输入确认 ZQ 校准请求,并指示该命令已从存储器控制器发送到外部 DDR

ui_clk 和 ui_clk_sync_rst 是提供给用户侧使用的时钟信号和同步复位信号。mmcm_locked 信号是 MIG IP 里面时钟锁相环的锁定信号输出。可通过观察这个信号是否变为高电平判断内部锁相环是否锁定。

sys_clk_i 是 IP 的系统时钟输入信号,根据前面 IP 配置,这个时钟需要提供 200MHz时钟, sys_rst 是 IP 的系统复位输入信号,低电平复位。

带 s_axi 的信号是供用户侧使用的 AXI 接口。从 IP 例化模板端口信号可以看到AXI 接口总线共有 5 个通道,分别是 Read Address Ports、Write Address Ports、Read Data Ports、Write Data Ports、 Write Response Ports。每一个 AXI 传输通道都是单方向的。接口信号中以"s_axi_aw"开头的是写地址通道信号,以"s_axi_w" 开头的是写数据通道信号,以"s_axi_b"开头的是写响应通道信号,以"s_axi_rw"开头的是读地址通道信号,以"s_axi_r"开头的是读数据通道信号。 5 个通道分为两个事务,写事务和读事务。

读写流程分析

读写流程:

5 条独立的通道都包含一个双路的 VALD、 READY 握手机制。信息源通过 VALID信号来指示通道中的数据和控制信息什么时候有效。目地源用 READY 信号来表示何时准备好接收数据。传输地址信息和数据都是在 VALID 和 READY 同时为高时有效。

读数据和写数据通道都包括一个 LAST 信号,用来指明一个事物传输的最后一个数据。

读/写事务都有自己的地址通道,地址通道携带着传输事务所必须的地址和信息。

读数据通道传送着从设备到主机的读数据和读响应信息。读响应信息指明读事务的完成状态。

写数据通路传送着主机向设备的写数据和写控制信息。写响应通道提供了设备响应写事务的一种方式。在每一次突发式写会产生一个完成信号

突发读时序:

主机发送地址和控制信息到写地址通道中,当地址通道上 ARVALID 和 ARREADY 同时为高时,地址 A 被有效的传给设备,之后设备输出的数据将出现在读数据通道上。当RREADY 和 RVALID 同时为高的时候表明有效的数据传输,从图中可以看到传输了 4 个数据。为了表明一次突发式读写的完成,设备用 RLAST 信号变高电平来表示最后一个被传输的数据, D(A3)是本次读突发的最后一个读数据。

突发写操作:

写操作的开始时,主机发送地址和控制信息到写地址通道中,当地址通道上 AWVALID和 AWREADY 同时为高时,地址 A 被有效的传给设备。然后主机发送每一个写数据到写数据通道中,当 WREADY 和 WVALID 同时为高的时候表明一个有效的写数据,当主机发送最后一个数据时, WLAST 信号就变为高。当设备接收完所有数据之后,设备将一个写响应发送回主机来表明写事务完成,当 BVALID 和 BREADY 同时为高的时候表明有效的响应。

AXI

AXI 协议支持乱序传输。每一个通过接口的事务有一个 IDtag。协议要求相同 ID tag 的事务必须有序完成,而不同 ID tag 可以乱序完成

写地址通道信号:

写数据通道信号:

写响应通道信号:

读地址通道信号:

读数据通道信号:

相关推荐
Charles Ray22 分钟前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
重生之我在20年代敲代码23 分钟前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记
我要吐泡泡了哦1 小时前
GAMES104:15 游戏引擎的玩法系统基础-学习笔记
笔记·学习·游戏引擎
骑鱼过海的猫1231 小时前
【tomcat】tomcat学习笔记
笔记·学习·tomcat
一口一口吃成大V2 小时前
FPGA随记——FPGA时序优化小经验
fpga开发
北岛寒沫3 小时前
JavaScript(JS)学习笔记 1(简单介绍 注释和输入输出语句 变量 数据类型 运算符 流程控制 数组)
javascript·笔记·学习
烟雨666_java3 小时前
JDBC笔记
笔记
GEEKVIP4 小时前
Android 恢复挑战和解决方案:如何从 Android 设备恢复删除的文件
android·笔记·安全·macos·智能手机·电脑·笔记本电脑
铁匠匠匠5 小时前
从零开始学数据结构系列之第六章《排序简介》
c语言·数据结构·经验分享·笔记·学习·开源·课程设计