南京观海微电子---快速上手DDR读写例程——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:;    endcaseend 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        endcaseend   //==========================================================================//==    其他//==========================================================================//读信号有效,且读出的数不是写入的数时,将错误标志位拉高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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/739212.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

javaEE8(数据库编程课后习题1,2)

一&#xff1a; 二&#xff1a; 数据库内信息&#xff1a; 登录&#xff1a; 注册&#xff1a; Register.jsp <% page pageEncoding"UTF-8" import"java.sql.*"%> <!DOCTYPE html> <html lang"en"> <head> <…

未知生,焉知死!小动物S了,如何处理?一个人一生该坚持的几件事——早读(逆天打工人爬取热门微信文章解读)

天地不仁&#xff0c;以万物为刍狗 引言Python 代码第一篇 人民日报 【夜读】一个人一生该坚持的几件事第二篇 人民日报 来了&#xff01;新闻早班车要闻社会政策 结尾 “未知生&#xff0c;焉知死” 曾经视为定数的冰冷生命 在热烈的内心烛照下 应当焕发出滚烫的热情 唯有热爱…

注意力、自注意力和多头注意力的区别

本文作者&#xff1a; slience_me 注意力、自注意力和多头注意力的区别 理解注意力&#xff08;Attention&#xff09;、自注意力&#xff08;Self-Attention&#xff09;和多头注意力&#xff08;Multi-Head Attention&#xff09;之间的区别非常重要&#xff0c;因为它们是自…

[HackMyVm] Quick

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 (Un…

nginx禁止国外ip访问

1.安装geoip2扩展依赖 yum install libmaxminddb-devel -y 2.下载ngx_http_geoip2_module模块 https://github.com/leev/ngx_http_geoip2_module.git 3.编译安装 ./configure --add-module/datasdb/ngx_http_geoip2_module-3.4 4.下载最新数据库文件 模块安装成功后,还要…

【阿里云系列】-基于云效构建部署NodeJS项目到ACK

准备工作 01、编写Dockerfile文件可以根据不同的环境&#xff0c;新建不同的Dockerfile文件&#xff0c;比如Dockerfile-PROD # Deliver the dist folder with NginxFROM nginx:stable-alpine ENV LANGC.UTF-8 ENV TZAsia/ShanghaiCOPY dist/ /usr/share/nginx/html COPY ngi…

speexsdp消除回声

speexsdp需要几秒的滤波时间。我测试4-7秒。 GitHub - cpuimage/WebRTC_AECM: Acoustic Echo Canceller for Mobile Module Port From WebRTC 更快的消除 webrtc_aecm 效果: 这是testecho.c样例的程序。 初始化函数&#xff1a; SpeexEchoState *speex_echo_state_init(in…

uniapp开发DAPP钱包应用(一) 环境搭建 Vue+ MetaMask + ABI.json

上几节我们讲了如何通过Java后端完成链上交易、信息查询、以及如何使用web3插件实现开发自测。 这一节&#xff0c;我们来说说前端DAPP的开发实现。 1. MeteMask &#x1fa9c;Java对接&#xff08;BSC&#xff09;币安链 | BNB与BEP20的开发实践&#xff08;三&#xff09;水…

Vue3全家桶 - VueRouter - 【6】导航守卫

导航守卫 查看以下情形&#xff1a; 点击主页链接时&#xff0c;默认情况下可直接进入指定页面&#xff0c;如下图&#xff0c;但是问题是该跳转的界面是需要用户登录后方可访问的&#xff1b; 可设置导航守卫来检测用户是否登录&#xff0c;如果已登录&#xff0c;则进入后台…

华为OD机试 - 模拟数据序列化传输(Java JS Python C C++)

题目描述 模拟一套简化的序列化传输方式,请实现下面的数据编码与解码过程 编码前数据格式为 [位置,类型,值],多个数据的时候用逗号分隔,位置仅支持数字,不考虑重复等场景;类型仅支持:Integer / String / Compose(Compose的数据类型表示该存储的数据也需要编码)编码后数…

四元数(Quaternion)的一些性质

四元数(Quaternion)是用于三维旋转和定向的四部分组成的超复数&#xff0c;超复数简单理解就是比abi这样的复数更复杂的复数&#xff0c;其中abi这样的复数我们也可以叫做二元数&#xff0c;表示复平面的一点&#xff0c;对于熟悉欧拉公式的朋友就知道&#xff0c;也可以看成是…

Sui与数据平台ZettaBlock达成合作,为其公测提供数据

Sui一向以闪电般的速度、无限水平扩展著称&#xff0c;现已迅速成为DeFi活动的重要场所。近期&#xff0c;数据平台ZettaBlock宣布在其开创性的Web3数据平台发布中&#xff0c;选择Sui作为基础集成合作伙伴之一。在ZettaBlock的开放测试版发布之际&#xff0c;构建者和开发者将…

双指针算法练习

27. 移除元素 题目 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑…

JS 事件捕获、事件冒泡、事件委托

js事件机制在开发中可以说时刻使用&#xff0c;例如dom绑定事件、监听其自身事件等。js事件机制有事件捕获、事件冒泡俩种机制&#xff0c;我们分别说下这俩种机制的使用场景。 一、概念 事件捕获顺序如下&#xff1a; window > document > body > div 事件冒泡顺序…

Using WebView from more than one process

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 未经允许不得转载 目录 一、导读二、概览三、问题过程源码追踪…

【C++进阶】C++继承概念详解

C继承详解 一&#xff0c;继承的概念和定义1.1 继承的概念1.2 继承的定义1.3 继承关系和访问限定符 二&#xff0c;基类和派生类的对象赋值转移三&#xff0c;继承的作用域四&#xff0c;派生类的默认成员函数五&#xff0c;继承和友元&静态成员和继承六&#xff0c;菱形继…

vue 在线预览word

1 mammoth 先找的是mammoth这个插件yarn add mammoth,版本是1,7.0 参考网上的示例使用如下&#xff1a; import mammoth from "mammoth"; const vHtml ref("") const readExcelFromRemoteFile (url) >{var xhr new XMLHttpRequest();xhr.open("…

柚见第十一期(前端页面开发)

创建队伍 便于控制样式,在外面套一层div 创建假数据模拟后端传来数据 //假数据模拟 const initFormData { "name": "", "description": "", "expireTime": "", "maxNum": 0, "passwor…

未来艺术展览新趋势——3D线上画展如何创新展示?

一、艺术展示的数字化转型 随着科技的不断进步&#xff0c;3D线上画展作为艺术展示的新趋势&#xff0c;正逐渐改变着人们欣赏和购买艺术作品的方式。对于画家而言&#xff0c;3D线上画展不仅提供了一个全新的平台来展示他们的作品&#xff0c;还开辟了销售渠道&#xff0c;扩大…

天梯赛的赛场安排(Python)

作者 陈越 单位 浙江大学 天梯赛使用 OMS 监考系统&#xff0c;需要将参赛队员安排到系统中的虚拟赛场里&#xff0c;并为每个赛场分配一位监考老师。每位监考老师需要联系自己赛场内队员对应的教练们&#xff0c;以便发放比赛账号。为了尽可能减少教练和监考的沟通负担&#…