本文依据的是xilinx的PG150文档,主要介绍的是xilinx的ultrascale系列中内存资源的使用。
一、方案概述
Xilinx UltraScale™架构中的DDR3/DDR4 SDRAM ip核旨在支持高性能的内存接口解决方案。这些ip可以用于将DDR3和DDR4 SDRAM内存类型集成到设计中,提供完整的内存控制器和物理层(PHY)解决方案。下面是该ip的大致架构:
可以看到在xilinx提供的DDR操作方案中,大致的设计方向与我们之前讨论的方式是一致的。同样是包含了DDRC(图中的Memory Controller)和DDRPYH(图中的Physcial Layer)结构。此外尽管图中没有体现,但是xilinx同样提供了AXI4 从接口来便于用户操作DDR。
二、方案架构
该方案的结构如下图所示:
对于一个DDR使用设计方案来讲,最重要的部分当然是DDRC的设计,也就是图中的Memory Controller部分。
这里的DDRC被设计用于从用户接口接收读写相关的所有事务,并将这些事务发送到DRAM,且使用尽可能少的FPGA资源。控制器在DRAM与系统时钟比为4:1的情况下运行,每个系统时钟周期可以发送一条激活(Activate)、一条列地址选通(CAS)和一条预充电(Precharge)命令。
控制器命令路径的关键模块包括:
Group FSMs(状态机组):这些状态机组负责将事务排队,检查DRAM时序,并决定何时请求发送预充电(Precharge)、激活(Activate)和CAS(列地址选通)命令。
“安全”逻辑和仲裁单元:这些逻辑单元在基于额外的DRAM时序检查对Group FSMs之间的事务进行重新排序的同时,确保所有DRAM命令请求能够持续向前进行。
最终仲裁器(Final Arbiter):最终仲裁器负责决定向物理层(PHY)发送哪些命令,并将决策结果反馈给前面的模块。
简单看了一下其他的部分,文档中讲的也比较简单,考虑到作为使用者,理解方案的具体实现原理不是我们的第一诉求,我们跳过这部分的内容,直接进入到大家喜闻乐见的使用环节。
三、方案使用
原文在这部分还介绍了一些时钟、复位信号的要求包括一些管脚和PCB相关的内容,我们同样不关心这部分的内容,直接进入到协议描述阶段,对于一个ip,我们最关心的就是它的功能和输入输出信号,功能我们简单了解了,接下来就来介绍接口信号。
这个ip提供了三种对外的接口形式供用户选择:
- User Interface
- AXI4 Slave Interface
- PHY Only Interface
3.1 User Interface
信号 | 输入/输出 | 描述 |
app_addr[APP_ADDR_WIDTH – 1:0]
| input | 表示当前请求的地址 |
app_cmd[2:0]
| input | 表示当前请求的指令,000表示数据写入,001表示数据读,011表示按byte写入 |
app_autoprecharge
| input | 这个信号会知识控制器在当前请求的DRAM CAS命令上设置A10自动预充电位 |
app_en
| input | 地址信号,指令信号的使能信号 |
app_rdy
| output | 该信号表示用户接口已经准备好接收指令了,如果在启用app_en时取消断言该信号,则必须重试当前的app_cmd、app_autoprecharge和app_addr,直到断言app_rdy为止。 |
app_hi_pri
| input | 保留,设置成0 |
app_rd_data [APP_DATA_WIDTH – 1:0]
| output | 读出的数据内容 |
app_rd_data_end
| output | 该信号拉高表示当前的时钟周期是app_rd_data上输出数据的最后一个周期 |
app_rd_data_valid
| output | 该信号拉高表示app_rd_data 数据有效 |
app_wdf_data [APP_DATA_WIDTH – 1:0]
| input | 写入的数据内容 |
app_wdf_end
| input | 该信号拉高表示当前的时钟周期是app_wdf_data上输入数据的最后一个周期 |
app_wdf_mask [APP_MASK_WIDTH – 1:0]
| input | 这为app_wdf_data提供了掩码。对于DDR3接口,app_wdf_mask端口出现在Vivado IDE中的“启用数据掩码”选项中。对于DDR4接口,app_wdf_mask端口出现在TRUE的“ECC”Vivado IDE选项值中。对于“ECC”Vivado IDE选项值为FALSE,端口显示为DM_NO_DBI和DM_DBI_RD。 |
app_wdf_rdy
| output | 此输出表明写入数据FIFO已经准备好接收数据。当app_wdf_rdy = 1‘b1和app_wdf_wren = 1’b1时,将接受写入数据 |
app_wdf_wren
| input | app_wdf_data的使能信号 |
app_ref_req
| input | 用户刷新请求 |
app_ref_ack
| output | 用户刷新请求完成 |
app_zq_req
| input | 用户ZQCS请求 |
app_zq_ack
| output | 用户ZQCS请求完成 |
ui_clk
| output | 用户始终频率必须是DRAM时钟频率的1/4 |
init_calib_complete
| output | PHY会在校准完成时断言init_calib_complete,只有当init_calib_complet拉高,用户的写入数据才有效 |
ui_clk_sync_rst
| output | 高有效复位 |
addn_ui_clkout1
| output | 根据用户需求提供额外的时钟输出 |
addn_ui_clkout2
| output | 根据用户需求提供额外的时钟输出 |
addn_ui_clkout3
| output | 根据用户需求提供额外的时钟输出 |
addn_ui_clkout4
| output | 根据用户需求提供额外的时钟输出 |
dbg_clk
| output | 调试时钟。不要将任何信号连接到dbg_clk,并保持端口打开 |
sl_iport0
| input[36:0] | 输入端口0(* KEEP =“true”*) |
sl_oport0
| output[16:0] | 输出端口0(* KEEP =“true”*) |
c0_ddr4_app_correct_en_i
| input | DDR4正确启用输入 |
有些信号的详细内容仅仅靠着表格中的三言两语很难讲清楚,这里我们另起篇幅,讲一下需要额外注意的信号内容。
3.1.1 地址信号 app_addr[APP_ADDR_WIDTH – 1:0]
此输入指示当前正在提交给用户界面的请求的地址。用户界面聚合了外部SDRAM的所有地址字段,并提供了一个平面的地址空间。 这个平面地址空间不是什么复杂的概念,其实就是把我们 已经知道的DDR的row,column,bank组合起来,形成一个信号罢了。但是怎么对这几个信号进行排序和长度划分却是需要用户参与考虑的。在内部,MEM_ADDR_ORDER负责定义这个信息,暴露给我们用户的视角,我们需要决定:
这里的 Memory Address Map 官方推荐就选择 ROW_COLUMN_BANK 形式,其他可供选择的内容还有:ROW_BANK_COLUMN,BANK_ROW_COLUMN和ROW_COLUMN_BAN_INTLV。这里我们就以ROW_COLUMN_BANK 为例,其他的形式可以查阅手册pg150中的 Table 4-15 到 Table 4-24 部分。这里需要注意的是app_addr的低3位映射到了列地址的低三位,这是用来表示 SDRAM 突发顺序的部分,由于控制器不支持突发排序,因此这些低阶位被忽略,使有效的最小app_addr步长hex 8。
3.1.2 写信号时序
对于写操作来说,支持以下3种情况:
- 写数据与写命令严格对齐
- 写数据提前写命令一个周期
- 写数据滞后于写命令,这种情况下,滞后时间最多支持2个时钟周期
3.2 AXI Slave Interface
AXI4从接口块将AXI4事务映射到用户接口(UI),为内存控制器提供一种行业标准的总线协议接口。AXI4从接口在通过DDR3/DDR4 SDRAM工具提供的设计中是可选的,且在这两个工具中的RTL实现是一致的。
3.2.1 可配置参数
对于这里的AXI4 从接口来说,官方提供了一些相关参数,这里我们进行一个简单的介绍:
参数名称 | 可选值 | 描述 |
C_S_AXI_ADDR_WIDTH
|
DDR3: 25–35
DDR4: 27–37
| 地址信号线的位宽。 它取决于内存密度和所选择的配置。 计算方式为: DDR3:log2(RANKS)+ROW_WIDTH+COL_WIDTH+BANK_WIDTH+log2(PAYLOAD_WIDTH)-3 DDR4:log2(RANKS)+ROW_WIDTH+COL_WIDTH+BANK_WIDTH+BANK_GROUP_WIDTH+log2(PAYLOAD_WIDTH)-3 PAYLOAD_WIDTH PAYLOAD_WIDTH含义是外部存储器接口的数据宽度,对于AXI设计,它被限制为8、16、32或64 |
_S_AXI_DATA_WIDTH
|
32, 64, 128, 256, 512
| 数据信号的宽度 |
C_S_AXI_ID_WIDTH
| 1-16 | 每个通道的ID信号的位宽 |
C_S_AXI_SUPPORTS_NARROW_BURST
| 0,1 | 此参数仅适用于C_S_AXI_DATA_WIDTH等于APP_DATA_WIDTH时。当C_S_AXI_DATA_WIDTH等于APP_DATA_WIDTH并且启用此参数时,AXI从服务器将实例化一个升级器。当主服务器发送AXI窄传输(比传输数据总线窄的传输)时,升级器打包连续传输,在用户界面呈现单个请求。因此,如果这个AXI从服务器可以接收窄段传输,则必须启用参数C_S_AXI_SUPPORTS_NARROW_BURST。如果不是,当从属接收到窄传输时,它会导致意外行为。当C_S_AXI_DATA_WIDTH等于APP_DATA_WIDTH并且已知AXI从服务器从未接收到窄传输时,可以禁用此参数以避免升级器的实例化,从而节省实现区域。在这种情况下,确保在实际模拟期间AXI从线从不接收窄传输。当C_S_AXI_DATA_WIDTH小于APP_DATA_WIDTH时,总是实例化升级器,且此参数无效。 |
C_RD_WR_ARB_ALGORITHM
|
TDM, ROUND_ROBIN,
RD_PRI_REG,
RD_PRI_REG_STARVE_LIMIT,
WRITE_PRIORITY_REG,
WRITE_PRIORITY
| 仲裁算法的方案 |
C_ECC
|
ON, OFF
| 此参数指定是否为该设计启用了ECC。ECC只在72位设计时支持启用,而对所有其他数据宽度禁用 |
3.2.2 AXI从接口信号
除了个别信号,基本就是非常标准的AXI接口,从命名中就可以很直观地看出信号对应的是AXI的哪个通道,下表通过颜色对不同的信号做了一个简单的区分。
- 写地址通道:红色
- 写数据通道:黄色
- 写响应通道:蓝色
- 读地址通道:紫色
- 读数据通道:绿色
名称 | 位宽 | 输入/输出 | 有效状态 | 描述 |
ui_clk | 1 | O | 时钟信号,由内存控制器提供到ui接口,AXI接口信号同步于ui_clk | |
ui_clk_sync_rst | 1 | O | 高 | 复位信号,由内存控制器提供到ui接口,AXI接口信号同步于ui_clk |
aresetn | 1 | I | 低 | AXI Shim的输入复位信号,应与FPGA逻辑时钟同步 |
s_axi_awid | C_S_AXI_ID_WIDTH | I | 写地址ID | |
s_axi_awaddr | C_S_AXI_ADDR_WIDTH | I | 写地址 | |
s_axi_awlen | 8 | I | 突发长度,代表一次突发中传输的数量 | |
s_axi_awsize | 3 | I | 突发大小,代表表示突发中每个传输的大小 | |
s_axi_awburst | 2 | I | 突发类型,仅支持INCR/WRAP | |
s_axi_awlock | 1 | I | 锁类型(当前实现未使用) | |
s_axi_awcache | 4 | I | 缓存类型(当前实现未使用) | |
s_axi_awprot | 3 | I | 保护类型(当前实现未使用) | |
s_axi_awqos | 4 | I | 服务质量(当前实现未使用) | |
s_axi_awvalid | 1 | I | 高 | 写地址有效,该信号表示有效的写地址和控制信息已可用 |
s_axi_awready | 1 | O | 高 | 写地址就绪,该信号表示从机已准备好接受地址和相关控制信号 |
s_axi_wdata | C_S_AXI_DATA_WIDTH | I | 写数据 | |
s_axi_wstrb | C_S_AXI_DATA_WIDTH/8 | I | 写选通 | |
s_axi_wlast | 1 | I | 高 | 写最后一个信号,该信号表示写突发传输中的最后一次传输 |
s_axi_wvalid | 1 | I | 高 | 写有效信号,该信号表示写数据和触发信号已准备好 |
s_axi_wready | 1 | O | 高 | 写就绪信号,该信号表示从机已准备好接收写数据 |
s_axi_bid | C_S_AXI_ID_WIDTH | O | 响应ID,该信号是写响应的识别标签 | |
s_axi_bresp | 2 | O | 写响应信号,该信号表示写响应的状态 | |
s_axi_bvalid | 1 | O | 高 | 写响应有效信号 |
s_axi_bready | 1 | I | 高 | 响应就绪信号 |
s_axi_arid | C_S_AXI_ID_WIDTH | I | 读地址ID | |
s_axi_araddr | C_S_AXI_ADDR_WIDTH | I | 读地址 | |
s_axi_arlen | 8 | I | 读突发长度 | |
s_axi_arsize | 3 | I | 读突发大小 | |
s_axi_arburst | 2 | I | 读突发类型仅支持INCR/WRAP | |
s_axi_arlock | 1 | I | 锁类型(当前实现未使用) | |
s_axi_arcache | 4 | I | 缓存类型(当前实现未使用) | |
s_axi_arprot | 3 | I | 保护类型(当前实现未使用) | |
s_axi_arqos | 4 | I | 服务质量(当前实现未使用) | |
s_axi_arvalid | 1 | I | 高 | 读地址有效信号 |
s_axi_arready | 1 | O | 高 | 读地址就绪信号 |
s_axi_rid | C_S_AXI_ID_WIDTH | O | 读ID标签 | |
s_axi_rdata | C_S_AXI_DATA_WIDTH | O | 读数据 | |
s_axi_rresp | 2 | O | 读响应 | |
s_axi_rlast | 1 | O | 读最后一个信号 | |
s_axi_rvalid | 1 | O | 读有效信号 | |
s_axi_rready | 1 | I | 读就绪信号 | |
dbg_clk | 1 | O | 调试时钟,不要连接任何信号到该信号引脚,保持引脚在实例化时开放 |
3.2.3 读写传输示例
3.2.3.1 完整写操作
3.2.3.2 完整读操作
3.2.3.3 窄传输写操作
3.2.3.4 窄传输读操作