南京观海微电子---快速上手DDR读写例程——DDR接口专栏(三)

简介: 本文介绍如何使用FPGA中DDR IP核的Native接口进行读写操作,详细解析用户接口信号及时序控制,并结合Verilog代码实现DDR3的初始化、连续写入与读取验证,帮助开发者掌握DDR高速存储应用。

1. 前言

本文将向大家介绍如何使用DDR IP核的Native接口来对DDR进行读写操作。

2. DDR IP核接口介绍

要想把DDR3 IP核使用起来,必先需要了解下该IP核有哪些接口。DDR3 IP核接口图如下所示。注:图中展示的为DDR IP的Native接口,除了Native接口,该IP核还支持AXI4接口。

图中黄色的区域为“用户接口(UI)”,是DDR IP核对外的读写接口。绿色的框是用户通过代码写的逻辑电路,用户逻辑直接操作“用户接口”,实现对DDR数据的读写。右边蓝色的框是FPGA与DDR颗粒之间的物理接口。

下表列出了用户接口的定义清单,其中被黄色标注出来的几个信号是用户需要重点操作的信号。

  • app_addr:当前读写请求地址;
  • app_cmd:当前读写请求命令,读命令为3’b001,写命令为3’b000;
  • app_en:高有效,使能app_cmd、app_addr、app_sz和app_hi_pri信号;
  • app_rdy:该信号指示用户接口(UI)是否可以接收命令。当app_en使能时,如果该信号无效,则app_cmd、app_addr必须保持,等待app_rdy信号有效时再释放;
  • app_rd_data:用户接口读回数据;
  • app_rd_data_end:高有效,指示当前app_rd_data为最后一个数据,该信号仅当app_rd_data_valid为高时有效;
  • app_rd_data_valid:高有效,指示app_rd_data数据有效;
  • app_wdf_end:高有效,指示当前app_wdf_data为最后一个数据;
  • app_wdf_mask:提供app_wdf_data屏蔽码;
  • app_wdf_rdy:指示UI可以接收写数据写入;
  • app_wdf_wren:高有效,指示app_wdf_data数据有效;
  • app_wdf_data:用户待写入DDR的数据。

以上接口功能描述的翻译仅供参考,详细的还是参考上面两张官方的表格。

3. 用户接口的读写时序

操作DDR的UI接口时,要特别注意app_rdy信号的状态。如下图所示,app_cmd、app_addr和app_en分别给了3次写addr0地址的指令。但指令只有在app_rdy为高时才被接受,即图中只有前两次写指令写给了DDR IP核。

3.1 写数据至DDR

在本文讲述读写DDR时序时,均采用的是4:1模式,即FPGA的用户逻辑采用时钟频率为DDR工作频率的四分之一,该设置需要在建立DDR IP时进行设置,如不了解,可以参考上一篇文章《MIG IP核的使用——DDR接口专栏(二)》。

往DDR UI接口写数据时,时序如下图所示:

图中上半部给出了写控制指令的操作时序图,下半部分别有3个虚线框,给出了3种写数据总线的操作时序图。方案1为作者推荐的方法,即写数据操作和写指令操作对齐。方案2意思为写数据操作可以提前写指令操作一个时钟周期。方案3表明,写数据操作最多可以落后写指令操作两个时钟周期。

上图给了一个实际的写数据例子。红色框为写指令的操作,总共向8个地址(0x0a00~0x0a38)进行写操作。由于第1个时刻到第6个时刻app_rdy一直为高,所以往地址0x0a00~0x0a28写指令立即写入了DDR IP核的FIFO中。但由于第7个时刻app_rdy突然拉低,往地址0x0a30写数据的指令没有成功写入DDR IP核中,因此必须等待。此时app_addr、app_en、app_cmd这些信号都必须保持,直到app_rdy再度拉高,该指令才会被成功写入给DDR。

蓝色框为写具体的DDR数据操作,由于app_wdf_rdy一直为高,因此UI接口上一次性将8个数据都写入到DDR IP核的数据缓存FIFO中。

显然上面实例是采用方案3的写数据方式,但作者还是推荐初学者采用方案1的方式写数据。即判断app_rdy和app_wdf_rdy都为高时,再同时写入指令和数据。

3.2 读数据

从DDR UI接口读数据时,时序如下图所示:

图中上半部给出了读控制指令的操作时序图,下半部分为读出的数据结果。从读指令被UI接口接收后到数据被读出来的延时时间是随机的,没有具体对应关系。

4. 读写DDR例程代码

话不多说,上读写DDR例程代码吧。本文引用了CSDN博主“孤独的单刀”编写的代码。这段代码非常好的向大家展示了UI接口的使用方法。

代码功能描述:(1)等待DDR初始化成功;(2)往DDR的地址连续写入了1024个数据;(3)从DDR中读出刚写入相同地址段的数据,并进行比对。

//**************************************************************************

// *** 名称 : ddr3_rw

// *** 作者 : 孤独的单刀

// *** 博客 : https://blog.csdn.net/wuzhikaidetb

// *** 日期 : 2021.12

// *** 描述 : 对DDR3进行循环读写

//**************************************************************************


//============================< 端口 >======================================

module ddr3_rw #

(

 parameter  integer          WR_LEN    = 1024    ,  //读、写长度

 parameter  integer          DATA_WIDTH  = 128   ,  //数据位宽,突发长度为8,16bit,共128bit

 parameter  integer          ADDR_WIDTH  = 28       //根据MIG例化而来

)(  

//DDR3相关 ------------------------------------------------------      

   input                         ui_clk               ,  //用户时钟

   input                         ui_clk_sync_rst      ,  //复位,高有效

   input                         init_calib_complete  ,  //DDR3初始化完成

//DDR3相关 ------------------------------------------------------  

   input                         app_rdy              ,  //MIG 命令接收准备好标致

   input                         app_wdf_rdy          ,  //MIG数据接收准备好

   input                         app_rd_data_valid    ,  //读数据有效

   input    [DATA_WIDTH - 1:0]   app_rd_data          ,  //用户读数据

   output  reg  [ADDR_WIDTH - 1:0]    app_addr        ,  //DDR3地址                      

   output                        app_en               ,  //MIG IP发送命令使能

   output                        app_wdf_wren         ,  //用户写数据使能

   output                        app_wdf_end          ,  //突发写当前时钟最后一个数据

   output    [2:0]               app_cmd              ,  //MIG IP核操作命令,读或者写

   output  reg  [DATA_WIDTH - 1:0]    app_wdf_data    ,  //用户写数据

//指示 ----------------------------------------------------------    

   output  reg                   error_flag                  //读写错误标志

   );


//============================< 信号定义 >======================================

//测试状态机-----------------------------------------        

localparam          IDLE  = 4'b0001    ;              //空闲状态

localparam          WRITE = 4'b0010    ;              //写状态

localparam          WAIT  = 4'b0100    ;              //读到写过度等待

localparam          READ  = 4'b1000    ;              //读状态

//reg define ----------------------------------------

reg  [3:0]          cur_state        ;        //三段式状态机现态

reg  [3:0]          next_state       ;        //三段式状态机次态

reg  [ADDR_WIDTH - 1:0]    rd_addr_cnt     ;        //用户读地址计数

reg  [ADDR_WIDTH - 1:0]    wr_addr_cnt     ;        //用户写地址计数

reg  [ADDR_WIDTH - 1:0]    rd_cnt          ;        //实际读地址标记

//wire define ---------------------------------------                    

wire            error          ;        //读写错误标记

wire            rst_n          ;        //复位,低有效

wire            wr_proc        ;        //拉高表示写过程进行

wire            wr_last        ;        //拉高表示写入最后一个数据

wire            rd_addr_last   ;        //拉高表示是最后一个读地址


//*********************************************************************************************

//**                    main code

//**********************************************************************************************

//==========================================================================

//==    信号赋值

//==========================================================================  

assign rst_n = ~ui_clk_sync_rst;

//当MIG准备好后,用户同步准备好

assign app_en = app_rdy && ((cur_state == WRITE && app_wdf_rdy) || cur_state == READ);              

//写指令,命令接收和数据接收都准备好,此时拉高写使能

assign app_wdf_wren = (cur_state == WRITE) && wr_proc;

//由于DDR3芯片时钟和用户时钟的分频选择4:1,突发长度为8,故两个信号相同

assign app_wdf_end = app_wdf_wren;

assign app_cmd = (cur_state == READ) ? 3'd1 :3'd0;          //处于读的时候命令值为1,其他时候命令值为0  

assign wr_proc = ~app_cmd && app_rdy && app_wdf_rdy;        //拉高表示写过程进行

//处于写使能且是最后一个数据

assign wr_last = app_wdf_wren && (wr_addr_cnt == WR_LEN - 1) ;

//处于读指令、读有效且是最后一个数据

assign rd_addr_last = (rd_addr_cnt == WR_LEN - 1) && app_rdy && app_cmd;


//==========================================================================

//==    状态机

//==========================================================================    


always @(posedge ui_clk or negedge rst_n) begin

 if(~rst_n)

   cur_state <= IDLE;

 else

   cur_state <= next_state;

end


always @(*) begin

 if(~rst_n)

   next_state = IDLE;

 else

   case(cur_state)

     IDLE:

       if(init_calib_complete)   //MIG IP核初始化完成

         next_state = WRITE;        

       else        

         next_state = IDLE;        

     WRITE:        

       if(wr_last)               //写入最后一个数据

         next_state = WAIT;        

       else        

         next_state = WRITE;              

     WAIT:        

         next_state = READ;        

     READ:        

       if(rd_addr_last)          //写入最后一个读地址,数据读出需要时间

         next_state = IDLE;

       else

         next_state = READ;          

     default:;

   endcase

end


always @(posedge ui_clk or negedge rst_n) begin

   if(~rst_n) begin        

       app_wdf_data <= 0;    

       wr_addr_cnt  <= 0;      

       rd_addr_cnt  <= 0;      

       app_addr     <= 0;          

   end

 else

       case(cur_state)

           IDLE:begin

               app_wdf_data <= 0;  

               wr_addr_cnt  <= 0;    

               rd_addr_cnt  <= 0;      

               app_addr     <= 0;

            end

           WRITE:begin

               if(wr_proc)begin               //写条件满足

                   app_wdf_data <= app_wdf_data + 1;    //写数据自加

                   wr_addr_cnt  <= wr_addr_cnt + 1;     //写地址自加

                   app_addr     <= app_addr + 8;        //DDR3 地址加8

               end

               else begin                               //写条件不满足,保持当前值

                   app_wdf_data <= app_wdf_data;      

                   wr_addr_cnt  <= wr_addr_cnt;

                   app_addr     <= app_addr;

               end

             end

           WAIT:begin                                                  

               rd_addr_cnt <= 0;                    //读地址复位

               app_addr    <= 0;                    //DDR3读从地址0开始

             end

           READ:begin                                //读到设定的地址长度    

               if(app_rdy)begin                      //若MIG已经准备好,则开始读

                   rd_addr_cnt <= rd_addr_cnt + 1'd1;//用户地址每次加一

                   app_addr    <= app_addr + 8;      //DDR3地址加8

               end

               else begin                            //若MIG没准备好,则保持原值

                   rd_addr_cnt <= rd_addr_cnt;

                   app_addr    <= app_addr;

               end

             end

           default:begin

               app_wdf_data <= 0;

               wr_addr_cnt  <= 0;

               rd_addr_cnt  <= 0;

               app_addr     <= 0;

           end

       endcase

end  

//==========================================================================

//==    其他

//==========================================================================

//读信号有效,且读出的数不是写入的数时,将错误标志位拉高

assign error = (app_rd_data_valid && (rd_cnt!=app_rd_data));  


//寄存状态标志位

always @(posedge ui_clk or negedge rst_n) begin

   if(~rst_n)

       error_flag <= 0;

   else if(error)

       error_flag <= 1;

end


//对DDR3实际读数据个数编号计数

always @(posedge ui_clk or negedge rst_n) begin

   if(~rst_n)

       rd_cnt <= 0;

 //若计数到读写长度,且读有效,地址计数器则置0      

   else if(app_rd_data_valid && rd_cnt == WR_LEN - 1)

        rd_cnt <= 0;              

   else if (app_rd_data_valid )  //读有效情况下每个时钟+1

       rd_cnt <= rd_cnt + 1;

end


endmodule

相关文章
|
7天前
|
云安全 人工智能 自然语言处理
|
11天前
|
人工智能 Java API
Java 正式进入 Agentic AI 时代:Spring AI Alibaba 1.1 发布背后的技术演进
Spring AI Alibaba 1.1 正式发布,提供极简方式构建企业级AI智能体。基于ReactAgent核心,支持多智能体协作、上下文工程与生产级管控,助力开发者快速打造可靠、可扩展的智能应用。
990 35
|
5天前
|
机器学习/深度学习 人工智能 自然语言处理
Z-Image:冲击体验上限的下一代图像生成模型
通义实验室推出全新文生图模型Z-Image,以6B参数实现“快、稳、轻、准”突破。Turbo版本仅需8步亚秒级生成,支持16GB显存设备,中英双语理解与文字渲染尤为出色,真实感和美学表现媲美国际顶尖模型,被誉为“最值得关注的开源生图模型之一”。
667 4
|
7天前
|
机器学习/深度学习 人工智能 数据可视化
1秒生图!6B参数如何“以小博大”生成超真实图像?
Z-Image是6B参数开源图像生成模型,仅需16GB显存即可生成媲美百亿级模型的超真实图像,支持中英双语文本渲染与智能编辑,登顶Hugging Face趋势榜,首日下载破50万。
527 25
|
14天前
|
数据采集 人工智能 自然语言处理
Meta SAM3开源:让图像分割,听懂你的话
Meta发布并开源SAM 3,首个支持文本或视觉提示的统一图像视频分割模型,可精准分割“红色条纹伞”等开放词汇概念,覆盖400万独特概念,性能达人类水平75%–80%,推动视觉分割新突破。
859 59
Meta SAM3开源:让图像分割,听懂你的话
|
4天前
|
弹性计算 网络协议 Linux
阿里云ECS云服务器详细新手购买流程步骤(图文详解)
新手怎么购买阿里云服务器ECS?今天出一期阿里云服务器ECS自定义购买流程:图文全解析,阿里云服务器ECS购买流程图解,自定义购买ECS的设置选项是最复杂的,以自定义购买云服务器ECS为例,包括付费类型、地域、网络及可用区、实例、镜像、系统盘、数据盘、公网IP、安全组及登录凭证详细设置教程:
195 114
|
11天前
|
人工智能 前端开发 算法
大厂CIO独家分享:AI如何重塑开发者未来十年
在 AI 时代,若你还在紧盯代码量、执着于全栈工程师的招聘,或者仅凭技术贡献率来评判价值,执着于业务提效的比例而忽略产研价值,你很可能已经被所谓的“常识”困住了脚步。
576 50
大厂CIO独家分享:AI如何重塑开发者未来十年
|
7天前
|
存储 自然语言处理 测试技术
一行代码,让 Elasticsearch 集群瞬间雪崩——5000W 数据压测下的性能避坑全攻略
本文深入剖析 Elasticsearch 中模糊查询的三大陷阱及性能优化方案。通过5000 万级数据量下做了高压测试,用真实数据复刻事故现场,助力开发者规避“查询雪崩”,为您的业务保驾护航。
382 25