从零开始学习紫光同创FPGA——PGL22G开发板之DDR3 IP简单读写测试(六) - FPGA开发者技术社区 - 电子技术论坛 - 广受欢迎的专业电子论坛
分享 收藏 返回

从零开始学习紫光同创FPGA——PGL22G开发板之DDR3 IP简单读写测试(六)

1.DDR3 IP简单读写测试实验例程

1.1** 实验目的**

MES22GP 开发板上有一片 Micron 的 DDR3(MT41K256M16 TW107:P)内存组件,拥有 16bit 位宽的存储空间(MT41J系列是旧的产品,目前很多型号已经停产,后续替代就是MT41K系列。硬件上的差异是MT41K支持1.35V低电压,同时也兼容1.5V电压,所以可以用MT41K直接替换相应型号的MT41J芯片)。该DDR3 存储系统直接连接到了 PGL22G 的 Bank L1 及 Bank L2 上。PGL22G的DDR IP为硬核IP,需选择正确的IP添加。

本次实验目的为生成DDR3 IP,实现DDR3的基于AXI4的简单读写控制,了解其工作原理和用户接口,然后通过在线Debugger工具查看写入和读出的数据是否一致。
image.png

1.2** DDR3控制器简介**

HMIC_H IP 是深圳市紫光同创电子有限公司FPGA 产品中用于实现对SDRAM 读写而设计的IP,通过公司Pango Design Suite 套件(后文简称PDS)中IP Compiler 工具(后文简称IPC)例化生成IP 模块。
image.png

HMIC_H IP 系统框图如下图所示:
image.png

HMIC_H IP 包括了DDR Controller、DDR PHY 和 PLL,用户通过 AXI4 接口实现数据的 读写,通过 APB 接口可配置 DDR Controller 内部寄存器,PLL 用于产生需要的各种时钟。

AXI4 接口:HMIC_H IP 提供三组 AXI4 Host Port:AXI4 Port0(128bit)、AXI4 Port1(64bit)、

AXI4 Port2(64bit)。用户通过 HMIC_H IP 界面可以选择使能这三组 AXI4 Port。三组 AXI4 Host Port 均为标准 AXI4 接口。

APB 接口:HMIC_H IP 提供一个 APB 配置接口,通过该接口,可配置 DDR Controller 内部寄存器。HMIC_H IP 初始化完成后使能该接口。

详细的端口说明请点击IP配置界面的View Datasheet查看IP手册。

DDR的IP需要手动添加,操作流程请查阅文件目录1_Demo_document/工具使用篇的《03_IP核安装与查看用户指南》。
image.png

1.2.1** DDR3 IP例化流程简述**

打开IPC 软件,进入 IP 选择界面,如下图所示,选取 System/DDR/Hard 目录下的 Logos HMIC_H,然后在右侧页面设置 Instance Name 名称,并选择 FPGA 的器件类型。
image.png

IP 选择完成后点击Customize 进入Logos HMIC_H IP 参数设置界面,如下图所示,左边Symbol 为接口框图,右边为参数配置窗口:
image.png

参数配置完成后点击左上角的Generate按钮,生成 IP,即可生成相应于用户特定设置

的 HMIC_H IP 代码。生成 IP 的信息报告界面如下图所示:
image.png
IP 自带生成的.pds 文件和.fdc 文件仅供参考,需要根据实际单板进行修改。成功生成 IP 后会在生产IP时指定的Pathname 路径下输出如下文件:
image.png

1.2.2** DDR3 IP配置说明**

HMIC_H IP 配置分为四个页面,分别为 Step1: Basic Options,Step2: Memory Options,Step3: Interface Options,Step4: Summary,请务必按照该页面顺序配置。

1.2.2.1 Step 1: Basic Options

是 IP 的基本配置页面,页面如下图所示:
image.png
image.png
image.png

1.2.2.2 Step 2: Memory Options

是Memory 参数的配置页面,页面如下图所示:
image.png
image.png
image.png

1.2.2.3 Step 3: Interface Options

是接口参数的配置页面,页面如下图所示:
image.png
image.png

1.2.2.41 Step 4: Summary

用于打印当前的配置信息,不需要配置参数,页面如下图所示:
image.png

1.3** 实验源码**

DDR3 IP配置完成后会生成一个可用于例化的模块。

1.3.1** DDR3 IP 模块接口说明**

如下图所示为DDR3 IP的Memory Interface(PHY),不需要我们直接操作。
image.png

以下所示为外部输入时钟,复位,输出的用户时钟axi4 port0/port1/port2以及一些复位或者初始化完成的标志信号(可以通过连接LED灯来直观显示,更易观察)。
image.png

以下所示为AXI4协议的读写控制端口,也是用户可以直接操作用于控制DDR3读写的端口。
image.png

AXI4协议的读写控制这里不进行具体讲解。
image.png
image.png

1.3.2** DDR3 读写测试顶层模块设计**

顶层模块的输入输出端口便是DDR3例化模块中的Memory Interface(PHY)和一些直连LED灯的用于观察的标志信号,因为本次实验通过按键来控制开始向DDR3写入数据,所以还需要一个输入按键。
image.png

然后对应DDR3的AXI4读写我们单独用一个模块来控制,顶层模块中的例化如下所示。
image.png

使用按键,所以需要一个按键消抖模块,顶层模块中的例化如下所示。
image.png

1.3.3** DDR3 AXI4读写控制模块**

本次实验只是一个简单的读写测试实验,故可以将一些AXI4的信号配置为常量。
image.png

使用按键控制数据开始写入DDR3,通过一个移位寄存器来产生这个写标志。
image.png

使用状态机来控制写地址信号,写数据信号,读地址信号,读数据信号的产生及状态的切换和跳转。

当按键按下,写标志触发,状态机进入写地址状态,awvalid_0信号为高电平,当awready_0和awvalid_0同时为高电平时,写地址被有效写入,下一个周期awvalid_0为低电平同时状态机跳转到写数据的状态;

写数据状态中,wvalid_0为高电平,当wready_0和wvalid_0同时为高电平时,数据开始被写入,一共写入5~20总计16个数据(从0开始计数长度便为15)。当写到最后一个数据时wlast_0保持一个周期的高电平。这里我们用一个计数器来产生写入数据,当wready_0和wvalid_0同时为高电平时开始计数器开始自加,当计数到15时(最后一个写入数据),下一个周期计数器清零,状态机跳转至等待写响应的状态;
image.png
image.png

写响应的状态中,通过一个移位寄存器抓取写响应有效bvalid_0信号为高电平的时刻,当bvalid_0且bresp_0为2’b00(表示写响应ok)时触发读开始的标志,状态机进入读地址写入状态;
image.png

读地址写入状态中,arvalid_0为高电平,当arready_0和arvalid_0同时为高电平时,读地址被有效写入(地址与写数据地址一致),下一个周期arvalid_0为低电平同时状态机跳转到读数据的状态;

读数据状态中,当rvalid_0 和 rready_和 rlast_0均为高电平时,状态机跳转至最初的状态等待按键被再次按下。读出的数据可以通过在线调试Debugger工具来查看。
image.png

1.3.4DDR3 IP的时钟约束

IP 有 5 个时钟,分别为 pll_refclk_in、phy_clk、pll_aclk0、pll_aclk1、pll_aclk2、pll_pclk,其中 pll_refclk_in 是输入时钟,phy_clk、pll_aclk0、pll_aclk1、pll_aclk2、pll_pclk 都是 PLL 倍频得到,phy_clk 用作 HMIC_H 硬核的输入时钟,pll_aclk0 用做 AXI4 port0 的输入时钟,pll_aclk1 用做 AXI4 port1 的输入时钟,pll_aclk2 用做 AXI4 port2 的输入时钟,pll_pclk 用做APB port的输入时钟。phy_clk 是HMIC_H专用时钟,在IP内部使用,不允许外接使用。pll_pclk,pll_aclk_0,pll_aclk_1,pll_aclk_2 四路时钟供外部逻辑使用,彼此没有相位关系,都是异步时钟。

外部输入时钟约束如下。
image.png

Pll产生的时钟约束如下。
image.png

1.4实验现象

点击Debugger按钮,下载程序,便可通过Debugger工具进行在线调试,查看具体信号的波形情况。
image.png
image.png
image.png
image.png
image.png
image.png

按下开发板的按键,产生写触发信号,awvalid_0信号为高电平,当awready_0和awvalid_0同时为高电平时,awvalid_0拉低同时进入写数据状态,wvalid_0拉高,随后wready_0和wvalid_0同时为高电平时开始写入数据5~20,wlast_0在写入最后一个数据时拉高。一段时间后bvalid_0拉高且bresp_0为2’b00,表示写入数据成功,然后进入读数据状态。
image.png

读地址写入状态中,arvalid_0为高电平,当arready_0和arvalid_0同时为高电平时,读地址被有效写入(地址与写数据地址一致),下一个周期arvalid_0拉低同时状态机跳转到读数据的状态。
image.png
image.png
image.png

通过Debugger工具查看DDR3先写入后读出的数据是一致的,表明DDR3的读写测试正常。

同时在烧录程序后,可以观察LED灯的亮灭情况来查看DDR的PLL输出的时钟是否已经稳定,DDR PHY复位是否完成和DDR的控制器是否初始化成功。
image.png

ddr3 axi4 读写控制模块源码如下:

`timescale 1ps/1ps





module axi4_rw(





//   axi4 write&read ctrl


input          areset_0          ,              


input          aclk_0            ,              


// Write address


output [7:0]   awid_0            ,              


output reg    [31:0]  awaddr_0   ,       


output [7:0]   awlen_0           ,              


output [2:0]   awsize_0          ,              


output [1:0]   awburst_0         ,              


output         awlock_0          ,              


output  reg   awvalid_0          ,               


input          awready_0         ,              


output         awurgent_0        ,              


output         awpoison_0        ,              


// Write data


output reg  [127:0] wdata_0      ,         


output [15:0]  wstrb_0           ,              


output   reg    wlast_0          ,             


output   reg      wvalid_0       ,           


input          wready_0          ,              


// Write response


input [7:0]    bid_0             ,              


input [1:0]    bresp_0           ,              


input          bvalid_0          ,              


output         bready_0          ,              


// Read address


output [7:0]   arid_0            ,              


output reg [31:0]  araddr_0      ,          


output [7:0]   arlen_0           ,              


output [2:0]   arsize_0          ,              


output [1:0]   arburst_0         ,              


output         arlock_0          ,              


output   reg   arvalid_0         ,              


input          arready_0         ,              


output         arpoison_0        ,              


output         arurgent_0        ,              


// Read response


input [7:0]    rid_0             ,              


input [127:0]  rdata_0           ,              


input [1:0]    rresp_0           ,              


input          rlast_0           ,              


input          rvalid_0          ,              


output         rready_0          ,              


input          key_flag


);





assign  awid_0     = 8'b0 ;


assign  awlock_0   = 1'b0 ; 


assign  awurgent_0 = 1'b0 ; 


assign  awpoison_0 = 1'b0 ;


assign  bready_0   = 1'b1 ;


assign  wstrb_0    = {16{1'b1}} ;


assign  awsize_0   = 3'b100 ;


assign  awburst_0  = 2'd1 ; 


assign  awlen_0    = 15 ;


assign  bid_0      = 0 ;





assign  arid_0     = 8'b0 ;


assign  arlock_0   = 1'b0 ; 


assign  arurgent_0 = 1'b0 ; 


assign  arpoison_0 = 1'b0 ;


assign  arsize_0   = 3'b100 ;


assign  arburst_0  = 2'd1 ;


assign  rready_0   = 1 ;


assign  arlen_0    = 15 ;


assign  rid_0      = 0 ;





reg [10:0] cnt_w; /*synthesis PAP_MARK_DEBUG="1"*/


reg [1:0] w_key_reg;/*synthesis PAP_MARK_DEBUG="1"*/


reg [1:0] r_key_reg;/*synthesis PAP_MARK_DEBUG="1"*/


wire w_flag;/*synthesis PAP_MARK_DEBUG="1"*/


wire r_flag;/*synthesis PAP_MARK_DEBUG="1"*/


reg [2:0] state;





always@(posedge aclk_0)


if(areset_0)


    w_key_reg <= 2'b00;


else


    w_key_reg <= {w_key_reg[0],key_flag};





assign w_flag = (w_key_reg == 2'b01)?1:0;





always@(posedge aclk_0)


if(areset_0)


    r_key_reg <= 2'b00;


else


    r_key_reg <= {r_key_reg[0],bvalid_0};








assign r_flag = (r_key_reg == 2'b10 && bresp_0 == 2'b00)?1:0;








always@(posedge aclk_0)


if(w_flag)


    awaddr_0 <= 'd1;


else 


    awaddr_0 <= 'd2;








always@(posedge aclk_0)


if(areset_0 || wlast_0)


    wdata_0 <= 'd5;


else if(wvalid_0 && wready_0 )


    wdata_0 <= wdata_0 + 1;


else 


    wdata_0 <= wdata_0;





always@(posedge aclk_0)


if( wvalid_0 && wready_0 && cnt_w == awlen_0-1)


    wlast_0 <= 'd1;


else


    wlast_0 <= 'd0;





always@(posedge aclk_0)


if(areset_0 || wlast_0)


    cnt_w <= 'd0;


else if(wvalid_0 && wready_0)


    begin


    if(cnt_w == awlen_0)


        cnt_w <= 0;


    else


        cnt_w <= cnt_w + 1;


    end


else


    cnt_w <= 0;





always@(posedge aclk_0)


if(r_flag)


    araddr_0 <= 'd1;


else 


    araddr_0 <= 'd2;





always@(posedge aclk_0)


if(areset_0)


    begin


        state <= 0;


        awvalid_0 <= 0;


        wvalid_0 <= 0;


        arvalid_0 <= 0;


    end


else


    begin


        case(state)


        0:


            begin


                if( w_flag)


                    begin


                        state <= 1;


                    end


            end


        1:


            begin


                awvalid_0 <= 1;


                if(awvalid_0 && awready_0)


                    begin


                        awvalid_0 <= 0;


                        state <= 2;


                    end


            end


        2:


            begin


                if(wvalid_0 && wready_0 && wlast_0)


                    begin


                        state <= 3;


                        wvalid_0 <= 0;


                    end


                else


                    begin


                        wvalid_0 <= 1;


                    end


            end





        3:


            begin


                if(r_flag)


                    begin


                        state <= 4;


                    end


            end


        4:


            begin


                arvalid_0 <= 1;


                if(arvalid_0 && arready_0)


                    begin


                        arvalid_0 <= 0;


                        state <= 5;


                    end


            end


        5:


            begin


                if(rvalid_0 && rready_0 && rlast_0)


                    begin


                        state <= 0;


                    end


            end


        endcase





    end





endmodule

更多回帖

×
发帖