【原创】Xilinx_7Series_MIG控制器驱动DDR3

一、项目说明:


平台:XC7K325T板卡
DDR3:两片MT41J256M16TW-107,共1GB,数据总线32bit
环境:Vivado 2019.2
IP:Memory Interface Generator(MIG 7 Series)
官方手册:ug586  (7Series Devices Memory Interface Solutions v4.2)

 


二、DDR3型号及原理

本实验使用了两片镁光的 MT41J256M16TW-107  DDR3芯片:
单片数据总线为16bit、深度32M、8个Bank,总共16 x 32M x 8 = 512MB

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第1张图片

上图是具体不同型号的DDR3所对应的参数,可见以 256M16 结尾的存储器有 256M个深度、16bit位宽,256M中可拆分为8个Bank,每个Bank深度为32M,总共 32M x 8 x 16 = 512MB。
本实验中用了两片DDR3,将地址总线接一起,数据总线并列成32bit,总共1GB。

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第2张图片

上图是 256 Meg x 16的功能框图,中间的从Bank0~Bank7 memory array 的8个正方形就是是存储器内部的8个Bank。Row address MUX(行地址复用器)和Bank control logic(逻辑块控制器)共同控制Bank x row address latch and decoder(某逻辑块的行地址锁存),这样就锁定了将要操作的存储空间的Bank和row;再通过column address latch(列地址锁存)选通一列,这样就唯一地确定了DDR3中的一块存储空间。

 

三、7Series_MIG控制器

DDR SDRAM是以牺牲速率为代价换取同等面积下的存储大小,拥有比同等面积的SRAM更大的存储空间,但同时也导致了DDR的控制极为复杂,DDR是靠往电容中存储电荷来存储二进制信息,但电容总有漏电流存在,这就代表了电容中的数据不能长时间存在,所以在DDR的控制步骤中需要定时对数据空间充电(刷新),上图中的 Refresh counter 就是定时对DDR进行刷新所用的定时器。
DDR的操作如此复杂,在实际工程开发中不可能自己去一行一行编写驱动,包括在数字芯片设计中,对DDR的使用也均是购买其他公司已经成熟的IP。Xilinx公司就提供了一款DDR/SDRAM控制器IP:Memory Interface Generator(MIG),MIG 可以大大简化DDR操作时序,繁杂的DDR物理接口时序已经在IP中替我们完成了,我们需要做的只是对IP的 User Interface 进行操作,节省了开发时间。

如下图,是 用户端逻辑—MIG控制器—DDR3物理层 的连接关系图:
 

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第3张图片


四、MIG用户端口 

app_addr[ADDR_WIDTH – 1:0]:input信号,当前请求的地址数据。

app_cmd[2:0]:input信号,当前操作命令,3‘b000为写操作、3’b001为读操作。

app_en:input信号,高电平表示当前地址数据有效。

app_rdy:output信号,高电平表明UI已准备好接受命令,只有当此信号为1时读写操作才能被有效执行。比如假设地址数据使能、写数据使能,但 app_rdy 为0,也无法将数据写入MIG的FIFO中。

app_rd_data[APP_DATA_WIDTH – 1:0]:output信号,当前读取到的数据。

app_rd_data_end:output信号,此高电平有效输出指示当前时钟周期是app_rd_data []上输出数据的最后一个周期。 仅当app_rd_data_valid为active-High时有效。(这个信号在用户逻辑中没用)

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信号,当前输入数据的掩码,mask为1的位对应的数据将被屏蔽掉。(这个信号本实验用不到,写0)

app_wdf_rdy:output信号,该输出表明写数据FIFO已准备好接收数据。 当app_wdf_rdy = 1’b1和app_wdf_wren = 1’b1时,接受写入数据。

app_wdf_wren:input信号,高电平表示 app_wdf_data 数据有效。

init_calib_complete:output信号,高电平表示MIG控制器校准结束,可以对DDR进行操控(使用时必须先等待此信号,否则无法正确操作)

ui_clk:output信号,MIG输出的用户端时钟,在4:1模式下是DDR主频的1/4。

ui_clk_sync_rst:output信号,MIG输出的用户端复位信号,高电平复位。

rst:MIG复位信号。

clk:MIG输入时钟,也是DDR的主频。

其他信号用不到,不用关注。

 

五、DDR3端口

ddr3_dq:DDR3数据总线

ddr3_dqs_n:DDR3数据选通反相端

ddr3_dqs_p:DDR3数据选通同相端

ddr3_addr:DDR3地址总线

ddr3_ba:DDR3块地址输入

ddr3_ras_n、ddr3_cas_n、ddr3_we_n:DDR3命令输入端

ddr3_reset_n:DDR3复位

ddr3_ck_p:DDR3输入时钟同相端

ddr3_ck_n:DDR3输入时钟反相端

ddr3_cke:DDR3时钟使能

ddr3_cs_n:DDR3片选,用于使能命令解释器

ddr3_dm:DDR3输入数据掩码

ddr3_odt:DDR3启用内部终端匹配电阻

 

六、IP配置

在 IP Catalog 中搜索 Memory Interface Generator,双击打开出现以下页面,点击 Next ,出现 MIG Output Options 。
 

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第4张图片

 

1)MIG Output Options

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第5张图片

 

  1. 选择创建设计以创建新的内存控制器设计。 在“Component Name field”一栏中输入IP核组件名称。
  2. 选择要生成的控制器数量。 此选项确定其他页面的复制。
  3. DDR2和DDR3 SDRAM设计支持内存映射的AXI4接口。 AXI4接口仅支持Verilog语言。 如果需要AXI4接口,请在调用MIG工具之前在Vivado Design Suite中将语言选择为“ Verilog”。 如果未选择AXI4界面,则用户界面(UI)是主要界面。
  4. 单击“下一步”以显示“引脚兼容的FPGA”页面

 

2)Pin Compatible FPGAs

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第6张图片

 

引脚兼容的FPGA页面列出了所选系列中具有相同封装的FPGA。 如果从MIG工具生成的引脚需要与其他任何FPGA兼容,则应使用此选项来选择必须与引脚兼容的FPGA。
单击“下一步”显示“存储器类型选择”页面。

 

3)Memory Selection

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第7张图片

此页面显示所选FPGA系列支持的所有存储器类型。选择DDR3 SDRAM控制器类型,单击下一步以显示“控制器选项”页面。

 

4)Controller Options

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第8张图片

 

此页面显示了可以选择的各种控制器选项。
如果设计具有多个控制器,则对每个控制器重复“控制器选项”页面。 此页面最多分为九个部分。 分区数取决于所选的内存类型。 控制器选项页面还包含以下下拉菜单,用于修改设计的不同功能:

  1. 时钟周期此功能指示所有控制器的工作频率。频率Block受所选FPGA和器件速度等级等因素的限制。本实验使用400MHz(例化锁相环产生)。
  2. PHY与控制器时钟比率此功能确定物理层(存储器)时钟频率与控制器和用户界面时钟频率的比率。由于FPGA逻辑时序的限制,2:1的比率降低了最大存储器接口频率。2:1比例的用户界面数据总线宽度是物理内存接口宽度的四倍,而4:1比例的总线宽度是物理内存接口宽度的八倍。2:1的比例具有较低的延迟。4:1的比率对于获得最高数据速率是必要的,本实验使用4:  1模式。
  3. 内存类型此功能选择设计中使用的内存部分的类型。
  4. 内存部分此选项选择设计的内存部分。可以从列表中进行选择,也可以创建新的Memory part。
  5. 数据宽度可以根据之前选择的存储类型在此处选择数据宽度值。 该列表显示了所选part的所有支持的数据宽度。 可以选择数据宽度之一。 这些值通常是各个设备数据宽度的倍数。本实验数据位宽为32位
  6. Bank Machines数量列表显示所选设计配置支持的Bank Machines数量。本实验设置为4。
  7. 排序正常模式允许内存控制器将命令重新排序到内存,以获得最高的效率。 严格模式强制控制器以接收到的确切顺序执行命令。本实验设置为Normal。

 

5)Memory Parameter Option

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第9张图片

 

该功能允许选择各种有关存储模式的寄存器值。在初始化期间,将模式寄存器的值加载到加载模式寄存器中。

1读取突发类型和长度-DDR2和DDR3 SDRAM仅支持突发长度8(BL8)。

2输出驱动器阻抗控制-输出驱动器阻抗控制设置DRAM上的输出驱动器阻抗。 列出的选择取决于所选的特定DRAM。 RZQ为240Ω。 例如,如果选择RZQ / 6,则输出驱动阻抗为40Ω。

 

6)FPGA Options

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第10张图片

 

1系统时钟此选项为sys_clk信号对选择时钟类型(单端,差分或无缓冲区)。 选择“无缓冲区”选项时,不会在RTL代码中实例化IBUF原语,并且不会为系统时钟分配引脚。

如果在没有执行更改的情况下实现了从MIG为No Buffer选项生成的设计,则设计可能会由于未针对sys_clk_i信号实例化的IBUF而无法实现。 因此,对于无缓冲区方案,需要将sys_clk_i信号连接到内部时钟。

对于已经分配了系统输入时钟且满足第210页上的“时钟”中指定的所有规则的设计,必须选择“无缓冲区”选项。

2系统复位电平–可以选择系统复位的电平(sys_rst)。 如果将选项选择为active-Low,则参数RST_ACT_LOW设置为1;如果设置为active-High,则参数RST_ACT_LOW设置为0。

3降低I/O功耗该选项通过在控制器处于空闲状态时自动禁用DQ和DQS IBUF来降低平均I/O功耗。

4HR Bank的内部终端匹配电阻–内部终端选项可以设置为40、50或60Ω或禁用。 此选择仅适用于HR Bank。

 

然后在 IO planning Options 中选择 fix pin out ,在下一个页面中绑定xdc文件至DDR3引脚:

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第11张图片

 

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第12张图片

在上图中点击 Read XDC/UCF ,绑定xdc文件,确认无误后点击 Validate 验证IO绑定有效性,有效性验证通过后点击 Next 。
后续的几个页面与配置无关,均点击 Next 即可。


七、操作时序

1)命令路径

如下图所示,当用户逻辑app_en信号被断言并且app_rdy信号从UI被断言时,UI会接受命令并将其写入FIFO。 每当取消声明app_rdy时,UI都会忽略该命令。 用户逻辑需要将app_en以及有效的命令和地址值保持为高,直到断言app_rdy为止(断言在这里就是有效的意思)。
说白了就是当app_en和app_rdy都有效时app_addr才会被打入FIFO中,编写Verilog时可以在改变app_cmd后判断app_rdy信号是否有效,若有效则拉高app_en将命令打入。

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第13张图片

 

2)写数据路径 

如下图所示,写数据通道的地址和数据的时序各自独立,共兼容三种时序关系:
1.写数据与相应的写命令一起出现。
2.在相应的写入命令的前一个周期写入数据。
3.写数据在相应的写命令之后出现,但不应超过两个时钟周期的限制。

对于注册了写命令后输出的写数据,addr / cmd允许的最大数据延迟为两个时钟。(如下图,“注册写命令” 是指app_en拉高后再拉低,如果没拉低就没有最大延迟2个周期的限制)

当断言app_wdf_wren且app_wdf_rdy为高电平时,写数据被寄存在写FIFO中(图1-76)。 如果取消断言app_wdf_rdy,则用户逻辑需要将app_wdf_wren和app_wdf_end保持为高电平以及有效的app_wdf_data值,直到断言app_wdf_rdy。
实际上当app_wdf_rdy拉高的同时将app_wdf_wren和app_wdf_end立即拉高即可。

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第14张图片

 

3)读数据路径 

下图是4:  1模式下的读取时序,UI会按请求的顺序返回读取的数据,并且在断言app_rd_data_valid时有效。 app_rd_data_end信号指示每个读取命令突发的结束,并且在用户逻辑中不需要。
可以使用一个D触发器,在app_rd_data_valid断言时将app_rd_data的数据打入触发器中,但需要注意的是此时缓存后的数据和app_rd_data_valid差一个时钟,若想使用app_rd_data_valid指示其他模块,需要将其打一拍与缓存后的数据对齐。

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第15张图片

 


八 、DDR相关参数计算

1、Brust Length 8

突发传输(Brust):是指在相邻存储单元中连续进行数据传输,提高数据传输带宽的技术。
突发长度(Brust Length):指连续进行数据传输的次数
Brust Length 8(BL8):指突发长度为8,每次突发传输遍历连续的8个存储空间,每次突发传输结束后 app_addr 需要 +8,在本实验中单次传输32bit数据,所以一次BL8的突发传输读写的数据长度为8x32=256bit。

2、PHY与控制器时钟比率

DDR3是时钟双边沿采样,即一个时钟周期内的上升沿和下降沿均可以进行数据操作,相比于普通的SDRAM,可以将带宽提高2倍,本实验中的输入时钟为400MHz,那么MIG控制器对DDR3的接口速度为400M x 2 = 800M。

本实验中的时钟比率为4:1,则用户时钟 ui_clk 为 400MHz / 4=100MHz。那么如下图所示,一个用户时钟周期对应DDR3的4个时钟周期、8个时钟边沿,也就是在一个用户时钟周期内,DDR3可以被突发读写8次,也正好对连续的8个地址空间进行操作,将长度为256bit的数据突发8次,每次并行传输32bit。

 

九、DDR3读写的Verilog代码

 在本实验中,使用了一个状态机:在空闲状态 STATE_IDLE 接收到 init_calib_complete 的断言后等待1秒,进入写操作状态  STATE_WRITE ,在此状态中写1024个周期(不是1024个数据,因为中间有很多等待app_rdy的过程),然后进入等待状态 STATE_WAIT ,在此状态中将命令信号app_cmd先设置为读取模式,然后等待8个周期,进入读操作状态 STATE_READ ,在此状态中读1024个周期,然后进入结束状态 STATE_END ,在此状态中初始化操作信号,然后跳回读操作状态 STATE_WAIT  ,并一直读取下去(意思就是只复位后写入一次,然后一直在读)。

为什么本实验只写一次然后一直读呢?是因为要验证写入数据是否全部有效,所以就写了一次,怕多次写入后覆盖掉前面写操作中可能存在的漏写现象。

module ddr3_read_write
       (
        input wire          sys_clk         ,       // 系统时钟
        output wire         calib_done      ,       // 校验完成,拉高表示初始化结束
        input wire          rstn            ,       // 异步复位
        output wire         locked          ,       // MMCM锁定信号

        // DDR3物理接口
        inout  [31:0]       ddr3_dq         ,       // DDR3数据总线
        inout  [3:0]        ddr3_dqs_n      ,       // DDR3数据选通反相端
        inout  [3:0]        ddr3_dqs_p      ,       // DDR3数据选通同相端
        output [14:0]       ddr3_addr       ,       // DDR3地址总线
        output [2:0]        ddr3_ba         ,       // DDR3块地址输入
        output              ddr3_ras_n      ,       // DDR3命令输入端
        output              ddr3_cas_n      ,       // DDR3命令输入端
        output              ddr3_we_n       ,       // DDR3命令输入端
        output              ddr3_reset_n    ,       // DDR3复位
        output [0:0]        ddr3_ck_p       ,       // DDR3输入时钟同相端
        output [0:0]        ddr3_ck_n       ,       // DDR3输入时钟反相端
        output [0:0]        ddr3_cke        ,       // DDR3时钟使能
        output [0:0]        ddr3_cs_n       ,       // DDR3片选,用于使能command decoder
        output [3:0]        ddr3_dm         ,       // DDR3输入数据掩码
        output [0:0]        ddr3_odt                // DDR3启用内部终端匹配电阻
        );


    // MIG DDR3 IP核例化线网定义
    (*mark_debug="true"*) wire[28:0]        app_addr            ;       // 用户要访问的DDR3地址
    (*mark_debug="true"*) wire[2:0]         app_cmd             ;       // 命令:读、写命令
    (*mark_debug="true"*) wire              app_en              ;       // MIG IP用户接口使能
    (*mark_debug="true"*) wire[255:0]       app_wdf_data        ;       // 待写入数据
    (*mark_debug="true"*) wire              app_wdf_end         ;       // 与写使能一起使用
    (*mark_debug="true"*) wire              app_wdf_wren        ;       // 写使能
    (*mark_debug="true"*) wire [255:0]      app_rd_data         ;       // 读出的数据
    (*mark_debug="true"*) wire              app_rd_data_end     ;       // 
    (*mark_debug="true"*) wire              app_rd_data_valid   ;       // 读出的数据有效标志位
    (*mark_debug="true"*) wire              app_rdy             ;       // DDR3就绪信号,表示没有正在读写
    (*mark_debug="true"*) wire              app_wdf_rdy         ;       // 写就绪,表示没有正在写
    (*mark_debug="true"*) wire              ui_clk              ;       // IP对外提供的用户时钟
    wire ui_rst                                                 ;       // IP对外提供的复位信号
    wire ddr3_clk                                               ;       // MIG IP时钟信号
    

    // MIG DDR3 IP核例化register定义(状态机用)
    reg[28:0]       app_addr_reg                                ;       // 用户要访问的DDR3地址 
    reg[2:0]        app_cmd_reg                                 ;       // 命令:读、写命令
    reg             app_en_reg                                  ;       // MIG IP用户接口使能
    reg [255:0]     app_wdf_data_reg                            ;       // 待写入数据寄存器
   (*mark_debug="true"*)  reg [255:0]app_rd_data_reg            ;       // 读出的数据寄存器(滞后一拍)
   (*mark_debug="true"*)  reg  app_rd_data_valid_reg            ;       // 读出数据有效标志寄存器(与app_rd_data_reg对齐而滞后一拍)
   


// 寄存器与DDR3 IP核连线
assign     app_addr     = app_addr_reg      ;
assign     app_cmd      = app_cmd_reg       ;
assign     app_en       = app_en_reg        ;
assign     app_wdf_data = app_wdf_data_reg  ;
assign     app_wdf_wren = app_en_reg & app_rdy & app_wdf_rdy & ( app_cmd_reg==3'b0 )  ;
assign     app_wdf_end  = app_wdf_wren      ;        



// 状态机相关定义
(*mark_debug="true"*) reg [3:0]state_reg                ;     // 状态寄存器
reg [31:0]fsm_cnt_reg             ;     // 状态机计数器

parameter STATE_IDLE = 'd0        ;     // 空闲状态
parameter STATE_WRITE = 'd1       ;     // 写状态
parameter STATE_WAIT = 'd2        ;     // 写完等待状态
parameter STATE_READ = 'd3        ;     // 读状态
parameter STATE_END = 'd4         ;     // 结束状态
parameter FSM_IDLE_TIME = 100000000     ;     // 空闲状态持续的周期:1秒
parameter FSM_WRITE_TIME = 1024   ;     // 写数据持续的周期
parameter FSM_WAIT_TIME = 8       ;     // 写完等待的周期
parameter FSM_READ_TIME = 1024    ;     // 读状态持续的周期




// 三段状态机:状态转移
always@(posedge ui_clk or posedge ui_rst)begin
    if(ui_rst)begin
        state_reg<='d0;
        fsm_cnt_reg<='d0; 
    end
    else begin
        case (state_reg)
            STATE_IDLE:begin
                if(calib_done=='b1)begin
                    if(fsm_cnt_reg

 

十、ILA工具Debug 

在工程中开启 ILA分析仪核,加入mark_debug的信号,综合、布线、生成比特流,下载至板卡中。按下板卡上的复位键,在1秒之内点击 ILA中的 Run trigger 按钮 出现如下界面:

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第16张图片

上图为ILA抓取的DDR3正在进行写操作时的时序图 。可见,当app_rdy、app_wdf_wren、app_wdf_end和app_en均断言时,DDR3开始写入数据。

 

【原创】Xilinx_7Series_MIG控制器驱动DDR3_第17张图片

上图为ILA抓取的DDR3正在读取数据的时序图。可见,当 app_rd_data_valid 断言后,开始正确输出数据到 app_rd_data_reg 中。

 

 

你可能感兴趣的:(HDL专栏,#,FPGA,fpga/cpld,ddr,verilog,debug)