ZYNQ学习:PL端DDR3 读写测试

DDR3简介

        DDR SDRAM 的最大特点是双沿触发,即在时钟的上升沿和下降沿都能进行数据采集和发送,同样的工作时钟, DDR SDRAM 的读写速度可以比传统的 SDRAM 快一倍。本次实验使用的 DDR3 芯片是南亚的 NT5CC256M16ER-EKI, NT5CC256M16ER-EKI 的 bank 位宽为 3,行位宽为 15,列位宽为 10,所以它的地址大小等于 2^3*2^15*2^10(即 2^28=256M),数据位宽为 16bit,所以容量大小为 256M*16bit,也就是 512MByte。

        下图是 7 系列的 MIG IP 核结构框图。 MIG IP 核对外分出了两组接口。左侧是用户接口,就是用户(FPGA)同 MIG 交互的接口,用户只有充分掌握了这些接口才能操作 MIG。右侧为 DDR 物理芯片接口,负责产生具体的操作时序,并直接操作芯片管脚。这一侧用户只负责分配正确的管脚,其他不用关心。

        

        表中标明的信号的方向是以 MIG IP 核作为参照的,例如表格中的信号方向定义为输出,那么相对于用户端(FPGA)来说实际上是输入.

        首先检查 app_rdy,为高则表明此时 IP 核命令接收处于准备好状态,可以接收用户命令,在当前时钟拉高 app_en,同时发送命令(app_cmd)和地址(app_addr),此时命令和地址被写入。

如上图所示,写数据有三种情形均可以正确写入:

(1)写数据时序和写命令时序发生在同一拍;

(2)写数据时序比写命令时序提前一拍;

(3)写数据时序比写命令时序至多延迟两拍;

        结合上图,写时序总结如下:首先需要检查 app_wdf_rdy,该信号为高表明此时 IP 核数据接收处于准备完成状态,可以接收用户发过来的数据,在当前时钟拉高写使能(app_wdf_wren),给出写数据(app_wdf_data)。这样加上发起的写命令操作就可以成功向 IP 核写数据。

        对于 DDR3,内部配置采用了 8n(n 代表芯片位宽) prefetch(预取) 来实现高速读写。 这也导致了 DDR3的 Burst Length 一般都是 8。 8n Prefetch 可以使得内核时钟是 DDR 时钟的四分之一,这也是 Prefetch 的根本意义所在。 DDR3 内部存储单元容量是芯片位宽的八倍, 在 4:1 的 PHY 到内存控制器(Memory Controller)时钟比模式下, UI 数据端口( app_wdf_data) 宽度为芯片位宽的八倍。 所以此处写时序中, 写使能(app_wdf_wren)只需要拉高一个时钟周期(内存控制器时钟周期),写使能(app_wdf_wren)和(app_wdf_end)是同步的。而在 2:1 的 PHY 到内存控制器(Memory Controller) 时钟比模式下, UI 数据端口(app_wdf_data)宽度为芯片位宽的 8 倍。写使能( app_wdf_wren) 需要拉高两个时钟周期( 内存控制器时钟周期), (app_wdf_end) 就在写使能(app_wdf_wren) 拉高的第二个时钟周期拉高一个时钟周期。

        注意当数据比地址写入多很多时, app_wdf_rdy 会一直为低,当地址比数据写入多很多时, app_rdy 会一直为低。

        这里需要指出的是 DDR3 的读或者写操作都可以分为背靠背和非背靠背两种情形。背靠背,即读或者写每个时钟都连续进行,中间没有间隙。非背靠背写则是非连续的读写。对于背靠背写,其实也有三种情形,唯一点不同的是,它没有最大延迟限制,如下图所示。

        读时序比较简单,发出读命令后, 用户只需等待数据有效信号(app_rd_data_valid)拉高,为高表明此时数据总线上的数据是有效的返回数据。需要注意的是,在发出读命令后,有效读数据要晚若干周期才出现在数据总线上。下面是背靠背读的情况,如下图所示。

        这里还需要注意一点,在连续读的时候,读到的数据顺序跟请求的命令/地址是相对应的。通常使用DDR3 的时候,为了最大限度地提高 DDR3 效能,充分利用突发写的特点,非背靠背很少用,而更多地采用背靠背操作。本章实验的读写操作就是基于背靠背模式进行的。

实验任务

        本节的实验任务是先向 DDR3 的存储器中写入 1024 个数据,写完之后再从存储器中读取相同地址的数据,若初始化成功,则 PL_LED0 常亮,否则 PL_LED0 闪烁;读取的值全部正确则 PL_LED1 灯常亮,否则PL_LED1 灯闪烁。

程序设计

总体模块设计

         根据实验任务,可以大致规划出系统的控制流程:首先等待 DDR3 初始化完成后, FPGA 调用 ddr3测试数据模块向 ddr3 控制模块写入数据, 数据写入到控制模块下的 fifo 控制模块, fifo 控制模块将写入数据的数据进行位宽转换后,然后写入到写 fifo 中,当写 fifo 中的数据超过一定数量时, ddr 读写模块就将写fifo 中的数据写入到 ddr3。当读 fifo 中的数据少于一定的数据量时, ddr 读写模块就从 ddr3 中读出数据写入fifo 控制模块下的读 fifo 中, fifo 控制模块对读 fifo 的输出数据进行位宽转化后输出到 ddr3 测试数据模块, ddr3 测试数据模块将读出的数据和写入的数据进行对比,然后将对比的结果输出到 led 显示模块。由此画出系统的功能框图如下图所示:

ddr3 控制模块设计

        ddr3 控制模块主要完成 ddr3 读写模块(ddr3_rw)、 fifo 控制模块(ddr3_fifo_ctrl)和 MIG IP 核(mig_series)的例化。 ddr3 控制模块产生读写 MIG IP 核用户接口的时序,实现与 MIG IP 核的数据及信号交互。一方面负责与用户(FPGA)进行数据交互,另一方面还产生控制 DDR3 读写的各种时序,并实现对 DDR 芯片的读写操作。 ddr3 读写模块负责与 MIG 模块的命令和地址的交互,根据 fifo 控制模块中读写 fifo 的剩余数据量来切换 DDR3 的读写命令和地址。 MIG IP 核一边与用户端进行交互,另一边对芯片进行操作,以实现数据的存储。 fifo 控制模块负责对输入数据(写入写 fifo 的数据 wrdata) 和输出的数据(写入读 fifo 的数据app_rd_data) 进行时钟域的切换和位宽的转换。

        在“DDR3 读写测试实验”的程序中,读写操作地址都是 DDR3 的同一存储空间,如果只使用一个存储空间缓存图像数据,那么这么做虽然保证了数据不会出现错乱的问题,但是会导致当前读取的图像与上一次存入的图像存在交错.

为了避免这一情况,我们在 DDR3 的其它 BANK 中开辟一个相同大小的存储空间,使用乒乓操作的方式来写入和读取数据,所以本次实验在“DDR3 读写测试实验”的程序里做了改动。

        我们在 DDR 中开辟出 2 个存储空间进行乒乓操作用于缓存帧图像。在摄像头初始化结束后输出的第一个数据对应图像的第一个像素点,将其写入存储空间的首地址中。通过在 DDR 控制模块中对输出的图像数据进行计数,从而将它们分别写入相应的地址空间。计数达存入 DDR 的最大写地址后,完成一帧图像的存储,然后当帧复位到来时来切换 bank 以达到乒乓操作的目的,并同时回到存储空间的首地址继续下一帧图像的存储。在显示图像时, LCD 顶层模块从 DDR 存储空间的首地址开始读数据,同样对读过程进行计数,并将读取的图像数据分别显示到显示器相应的像素点位置。

        图像数据总是在两个存储空间之间不断切换写入,而读请求信号在读完当前存储空间后判断哪个存储空间没有被写入,然后去读取没有被写入的存储空间。对于本次程序设计来说,数据写入较慢而读出较快,因此会出现同一存储空间被读取多次的情况,但保证了读出的数据一定是一帧完整的图像而不是两帧数据拼接的图像。当正在读取其中一个缓存空间,另一个缓存空间已经写完,并开始切换写入下一个缓存空间时,由于图像数据读出的速度总是大于写入的速度,因此,读出的数据仍然是一帧完整的图像。

MIG IP核

        

图 40.4.9 时钟及物理芯片配置

        Clock Period: DDR3 芯片运行时钟周期,这个参数的范围和 FPGA 的芯片类型以及具体类型的速度等级有关。本实验选择 1250ps,对应 800M,这是本次实验所采用芯片可选的最大频率。注意这个时钟是 MIG IP 核产生,并输出给 DDR3 物理芯片使用的,它关系到 DDR3 芯片具体的运行带宽。比如本次实验的开发板板载了 2 颗 DDR3 芯片,数据位宽总共 32 位,因为是双沿触发,这里带宽达到了 51.2Gb/s(1600M*32bit)。

        PHY to Controller Clock Ratio: DDR3 物理芯片运行时钟和 MIG IP 核的用户端(FPGA)的时钟之比一般有 4:1 和 2:1 两个选项,本次实验选 4:1。由于 DDR 芯片的运行时钟是 800MHz,因此 MIG IP 核的用户时钟(ui_clk)就是 200MHz。一般来说高速传输的场合选择 4:1,要求低速的场合选择 2:1。这里还要指出,当 DDR3 时钟选择选择了 534M 以上的频率,比例默认只为 4:1,低于 534M 才有 4:1 和 2:1 两个选项。

        VCCAUX_IO: 这是 FPGA 高性能 bank(High Performance bank)的供电电压, Vccaux_io 必须设置为2.0V 以实现最高数据速率。在 FPGA 高范围 bank(High Range banks) 中无法使用 Vccaux_io。 因为本实验使用的是高范围 bank,所以未使用 Vccaux_io。

        Memory Type: DDR3 储存器类型选择。本实验选择 Component。

        Memory Voltage:是 DDR3 芯片的电压选择,本实验选 1.35V。

        Data Width:数据位宽选择,这里选择 32,因为是 2 块 ddr3 拼接而成的。

        Data Mask:数据屏蔽管脚使能。勾选它才会产生屏蔽信号,本实验没用到数据屏蔽,但是在这里还是把它勾选上。

        Number of Bank Machines: Bank Machine 的数量是用来对具体的每个或某几个来单独控制的,选择多了控制效率就会高,相应的占用的资源也多,本实验选择 8 个。

        ORDERING:该信号用来决定 MIG 控制器是否可以对它收到的指令进行重新排序,选择 Normal 则允许, Strict 则禁止。本实验选择 Normal,从而获得更高效率.

图 40.4.12 储存器配置

        Input Clock Period: MIG IP 核的系统输入时钟周期,该输入时钟是由 FPGA 内部产生的,本次实验选择的时钟频率为 5000ps(200MHz) 。

        Read Burst Type and Length:突发类型选择,突发类型有顺序突发和交叉突发两种,本实验选择顺序突发(Sequential),其突发长度固定为 8。

        Output Driver Impdance Control:输出阻抗控制。本实验选择 RZQ/7。

        RTT:终结电阻,可进行动态控制。本次实验选择 RZQ/4。

        Controller Chip Select Pin:片选管脚引出使能。本实验选择 enable,表示把片选信号 cs#引出来,由外部控制。

        BANK_ROW_COLUMN:寻址方式选择。本实验选择第二种,即 BANK-ROW-COLUMN 的形式,这是一种最常规的 DDR3 寻址方式,即要指定某个地址,先指定 bank,再指定行,最后指定列,这样就确定了 一 个 具 体 地 址 。 一 般 来 说 这 样 寻 址 方 式 有 利 于 降 低 功 耗 , 但 是读写性能 ( 效 率 )上不如“ROW_BANK_COLUMN”。

图 40.4.13 系统时钟配置

        System Clock: MIG IP 核输入时钟。本实验选择“No Buffer” , 因为 IP 核的输入系统时钟是单端时钟,是由内部的 MMCM 产生的, MMCM 所产生的时钟默认添加了 buffer。

        Reference Clock: MIG IP 核参考时钟。同样选择“No Buffer”,将由时钟模块生成。感兴趣的用户也可以选择“Use System Clock”这个选项,这时候的 MIG IP 系统时钟同时作为了参考时钟, IP 核参考时钟要求是 200Mhz,而 MIG IP 核的系统时钟刚好也使用了 200Mhz 的系统时钟。

        System Reset Polarity:复位有效电平选择。本实验选择“ACTIVE LOW”低电平有效。

        Debug Signals Control:该选项用于控制 MIG IP 核是否把一些调试信号引出来,它会自动添加到 ILA,这些信号包括一些 DDR3 芯片的校准状态信息。本实验选择选择“OFF”,不需要让 IP 核生产各种调试信号。

        Sample Data Depth:采样深度选择。当“Debug Signals Control”选择“OFF”时,所有采样深度是不可选的。

        Internal Vref:内部参考管脚,表示将某些参考管脚当成普通的输入管脚来用,仅限 800Mbps 以下速率使用,本节实验不可选。

        IO Power Reduction: IO 管脚节省功耗设置。本实验选择“ON”,即开启。

        XADC Instantiation: XADC 模块例化。使用 MIG IP 核运行的时候需要进行温度补偿,可以直接选择XADC 模块的温度数据引到 MIG IP 核来使用,否则需要额外提供温度数据,所以本实验选择“Enable”。

ddr 读写模块设计

        ddr 读写模块负责与 MIG IP 核的命令和地址的交互,根据 FIFO 控制模块中读写 fifo 的剩余数据量来切换 DDR3 的读写命令和地址。 ddr3 读写模块框图如下。

        DDR3 初始化完成后,处于 DDR3_DONE 状态,由于此时读写 fifo 中都无数据并且 DDR3 读使能(ddr3_read_valid)一直为高,会先进入读状态(READ),控制 MIG IP 核从 DDR3 中读取一次读突发长度的数据量,写入读 fifo 中,以此来保证读 fifo 中一直会有数据,避免出现读空。读完后进入 DDR3_DONE状态,若此时写 fifo 中有至少一次写突发长度的数据量,就会进入写状态,向 MIG IP 核传输一次突发长度的数据后写入到 DDR3 中。写入完成后又会进入到等待状态(DDR3_DONE)。总之,状态机会根据读写fifo 的数据余量进行读写状态的切换。下面来讲解具体的各状态,主要是写和读状态。

        写状态首先判断 MIG IP 核发送过来的信号 app_rdy 和 app_wdf_rdy,当这两个信号同时为高时,拉高app_en 和 app_wdf_wren,同时时给写出命令,即 app_cmd 为 0,此刻正式进行 DDR3 写过程。写的过程中,写数据 app_wdf_data 和地址计数 wr_addr_cnt 在每个时钟自加 1。另外大家注意写地址(app_addr_wr)每次要自加 8,前面其实提到,用户端在每一个用户时钟进行一个 256bit 的数据的传输,在 DDR3 物理芯片端需要分 8 次传输,每次传输一个地址位宽 32bit, 8 次就需要 8 个地址。

        在读状态,判断 MIG IP 核发送过来的信号 app_rdy,当这个信号为高时,拉高 app_en,同时给出读命令,即 app_cmd 为 1,此时开始进行读操作。在进行读操作的时候,读地址(app_addr_rd)同样每次自加 8。

        需要特别指出的是,在写的时候, app_en 拉高的条件是: app_rdy (MIG 命令接收准备好标志)和 app_wdf_rdy(MIG 数据接收准备好标致)同时为高,根据数据手册,其实用户可以只判断 app_rdy 信号,当 app_rdy 信号为高,就可以拉高使能 app_en,之所以同时判断 app_rdy 和app_wdf_rdy 是因为本实验只需要考虑 DDR3 写数据使能和写数据同时有效的情形,这样的做法基本上不会对 DDR3 读写操作效率造成很大影响,但是却能大大简化代码编写难度。

        上文说过 app_wdf_end 表明当前时钟是突发写过程的最后一个时钟周期,由于本实验采用的是 4:1 模式,每一个用户时钟周期实际上都完成了一个写突发操作,所以在突发写有效时, 即app_wdf_wren拉高,每个时钟, app_wdf_end也跟着拉高,在4:1模式信号app_wdf_end和app_wdf_wren是同步变化的

        从代码第 121 行可以看出在写 fifo 数据计数器在 30(本次实验读写突发长度都为 32)时状态机跳转到写状态,开始向 DDR3 写数据,这是因为本次实验在 fifo 控制模块中用的 FIFO 不是标准 FIFO 的模式,是First Word Fall Through 模式(此时 FIFO 的最大数据量为计数器最大值加 2),这种模式的特点是数据和读使能同时出来,也就是说前两个数据会被储存在 FIFO 的缓冲区,使得 FIFO 的数据计数器比实际上的数据量少 2。进入写状态时,由于本次实验, ddr3 读使能一直拉高,所以在读 fifo 数据计数器小于 30(原因同上)后,就会进入下一次读 DDR3,而此时 ddr3 数据测试模块继续读取读 fifo 中的数据,总之读 fifo 中的数据量会一直保持在 30 及以上。

        ddr 读写模块波形如下:

图 40.4.27 读 fifo 为空

        从上图可以看出,此时 ddr3 数据测试模块正在向写 fifo 写入数据,读 fifo 为空, ddr3 读写模块读 DDR3信号一直拉高,所以控制 MIG IP 核读取 DDR3 向读 fifo 写入数据,保持读 fifo 中一直有最少一次读突发长度的数据。

图 40.4.28 读 fifo 写数据

        从上图中可以看出, ddr3 读写模块控制 MIG IP 核向读 fifo 写入一次读突发长度的数据,读地址计数器计数到了 31(0~31 共 32 个)。而 MIG IP 核写入读 fifo 需要一定的时间,导致此时的读 fifo 数据量不足一次突发长度,状态机会再次进入到读状态,在第二次读操作结束时,读 fifo 一共会读取两次读突发长度(本次实验为 64)的数据量。所以读 fifo 中前 1024 个数据(64 个 256 位宽数据即 1024 个 16 位宽数据)是无效数据

图 40.4.29 写状态


图 40.4.30 读状态

        当写 fifo 的存储数据达到一次写操作的长度时,就将状态跳到写状态(WRITE),如图 40.4.29。当 rfifo里面的数据不满一次读操作的情况下,将状态跳转到读状态(READ),如图 40.4.30,否则留在 DDR3 空闲状态。在写状态,当写地址计数器达到一次写操作的时候并且握手信号命令使能 app_rdy 和写数据有效使能 app_wdf_rdy 同时为高的时候,将状态跳转到 DDR3 空闲状态。同理读状态也是如此,此处不在详述。

        在程序的第 146 行至 176 行,设计这几行的目的是为了保证在 FIFO 完全复位后再对 MIG IP 核进行操作,这样就防止了 FIFO 还没有复位完全就写入数据的情况,如下图所示,当复位结束后, full 信号还将持续一段时间,在这段时间内是不能写入数据的,必须让信号 raddr_rst_h 拉高一段时间。

图 41.4.14 rfifo 的复位时序图

        在程序的第 125 至 170 行是分别对 rd_fifo 和 wr_fifo 进行帧复位,这里进行复位是为了在帧结束后清空FIFO,保证下帧数据到来之前 FIFO 为空。细心的朋友应该发现了一般复位是一个时钟周期,为什么这里的复位是一段电平,因为 XILINX 的手册规定 FIFO 的复位周期必须大于读写时钟的 5 个时钟周期,才能使FIFO 完全复位

ddr测试数据模块设计

        ddr3 测试数据模块从写起始地址开始,连续向 1024 个存储空间中写入数据 1~1024。写完成后进行读操作,持续将该存储空间的数据读出,并从第二次读出的数据进行测试,根据测试结果输出错误信号(error)。

        需要注意的是程序中第 122 行通过变量 rd_valid 将第一次读出的 1024 个数据排除,并未参与读写测试。第一次读操作结束后,读 FIFO 中的无效数据被读出并丢弃,后续读 DDR3 得到的数据才用于验证读写过程是否正确,如下图所示:

图 40.4.33 写数据

        从上图可以看出, 在 rd_valid 拉高之前是不检测数据对错的,将读取的数据直接抛弃,当 rd_valid 信号拉高后开始检测数据正确与否,如下图所示:

图 40.4.34 读数据

        从上图中可以看到后续读出的数据与检测计数器的值一致,是一开始写入 DDR 的数据,所以本次读写是正确的。

仿真验证

        在顶层模块定义了最大和最小读写地址,在调用时数据在该地址空间中连续读写,也指定 ddr 控制器的一次读写长度,这里的长度是可以修改的,但必须能被写入 ddr3 的最大数据地址整除,也不要大于最大地址除 DDR3 突发长度的值。如果大于就会在从读取 DDR3 中数据(读取的是读 fifo 中的数据)时,出现前面两次读出的数据。

        进行仿真还需要官方提供的ddr3_model.sv(system verilog 文件,硬件的仿真文件)、 ddr3_model_parameters.vh(ddr3_model 参数头文件)和 wire_delay.v(控制双向总线),三个文件;注意本次实验使用的时钟模块为了生成时钟信号稳定需要复位信号至少拉低 100ns,由于数据总线是双向的,所以仿真中需要对数据总线进行控制。


    

### Zynq 平台 DDR 内存分配方法 #### 设计概述 DDR内存控制器是一个高度集成的组件,支持多种DDR内存类型(DDR2、DDR3DDR3L、LPDDR2),并通过精心设计的架构来优化内存访问效率[^1]。 #### 地址映射配置 对于Zynq平台上的DDR3内存,在Vivado工具中导出硬件时已经自动完成了地址映射。具体来说,`ps7_ddr_0`是系统为DDR3分配的地址空间,其大小通常设置为512MB即`0x1FF00000`字节[^2]。此默认配置适用于大多数应用场景,但在某些情况下可能需要调整以适应特定需求。 #### Linux内核中的SDRAM定义 当在Linux环境下工作时,可以通过修改内核源码中的宏定义来指定可用物理内存的数量。例如,在Zynq平台上可以找到如下代码片段: ```c #define CONFIG_SYS_SDRAM_SIZE (512 * 1024 * 1024) /* 根据实际内存大小修改 */ ``` 这段代码表明,默认情况下操作系统认为有512MB SDRAM可供使用[^3]。如果板载的实际DDR容量不同,则需相应更改该数值以匹配真实情况。 #### 中断处理函数示例 虽然这不直接涉及DDR内存分配,但对于理解整个系统的运作机制仍然重要。下面展示了一个简单的中断服务程序模板,它可以在发生特定事件时执行必要的操作: ```c static irqreturn_t interrupt_hook(int irq, void *dev_id, struct pt_regs *regs) { // 处理逻辑... } ``` 这个例子展示了如何编写一个基本的中断处理器,这对于管理外部设备与DDR之间的交互非常有用[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值