『FPGA通信接口』DDR(3)DDR3颗粒读写测试

在这里插入图片描述

文章目录

  • 前言
  • 1.配套工程简介
  • 2.测试内容与策略
  • 3. 测试程序分析
  • 4.程序结果分析
  • 5.一个IP控制两颗DDR3
  • 6.传送门

前言

以四颗MT41K512M16HA-125AIT颗粒为例,介绍如何在一块新制板卡上做关于DDR3的器件测试。前面两篇介绍了什么是DDR,并介绍了xilinx给出的FPGA与DDR完美结合的方案MIG IP核,请按照顺序阅读DDR相关文章,链接在文末。DDR3颗粒,DDR3内存条,DDR4颗粒,DDR4内存条都可以与FPGA相连,DDR芯片选型以及链接形式选型与系统对于数据带宽的要求,存储容量的要求,对结构的要求息息相关,同时不同形式不同代DDR对FPGA选型提出了要求,本文介绍DDR3颗粒与FPGA相连,实现读写测试

1.配套工程简介

提供了两套DDR3颗粒测试工程,工程文件名是DDR3_2PCS800MHz,DDR3_4PCS400MHz,对应2片DDR的800Mhz读写测试,4片DDR的400Hz读写测试。此外还有DDR4颗粒测试工程和DDR3内存条测试工程。两套DDR3颗粒测试工程主控芯片是xilinx xc7k325tffg900-2,颗粒是MT41K512M16HA-125AIT,vivado版本是2020.2。不同的速度测试实际上只需要修改IP里面的配置即可。2片与4片的区别是,一次DDR引脚时钟,写入DDR的数据量是32bit和64bit的区别;MIG IP是按照8倍突发工作,即一个ui_clk用户时钟,MIG读取8个地址的数据,所以2片与4片会导致MIG IP的数据总线是256bit和512bit的区别。为了读者能够快速应用到自己的场景,提供的工程做了区分。需要说明的是,工程应用在本地需要注意,切换相应的时钟输入,切换DDR的UCF文件,切换指示灯的链接,如果有更多的指示灯,则可以接TEST_LENGTH相关的计数器指示一次测试结束,或者指示MIG IP的初始化结果。如果FPGA的型号不同或者DDR颗粒不同,建议重新创建工程进行测试,并且根据对应芯片重新计算TEST_LENGTH。例程都是按照app接口进行控制,在DDR3_4PCS400MHz中提供了一个block design,可以置顶编译综合布线生成bit后,使用tcl的命令便捷读写指定位置,用在定位某个确定位置的读写,这个block design用的是DDR 的AXI接口。
在这里插入图片描述
需要注意,本例的DDR颗粒不在MIG IP所直接支持的列表中,因为通过查询手册自定义了一个DDR型号,如下图所示。
在这里插入图片描述

2.测试内容与策略

通过写入和读出判断读写过程是否正确;此外,工程实践中会遇到当工作频率高的时候DDR读写不稳定, 因此必要进行速度测试。此外还应进行容量进行测试。一个测试工程,对上述三项全部测试;在MIG的平面地址接口中,按照地址顺序写入确定的已知值,然后在按照相同顺序读出这些已知值做比较,相同则常亮led表示测试通过,否则led闪烁表示测试失败。

3. 测试程序分析

附verilog测试代码,省略了模块例化部分,程序步骤解读如下:
①例化clk ip产生200MMIG参考电压输入
②例化MIG ip通过app接口读写ddr数据
③写两段式状态机,为进行容量测试,写至满容量的90%即可。写完切换至读状态,若读写无误则一直读写。Wr_addr_cnt或者rd_addr_cnt每计数一次,app_addr_begin自增8,这是因为,当工作频率配置为800MHz且用户时钟ui_clk与工作频率的比值配置为4:1时,ui_clk为200MHz。四片DDR3的数据位宽为64bit,由于”“DDR(double data rate)”,所以在每一个800MHz周期应该提供128bit数据,因此每一个200MHz周期应该向MIG提供512bit数据。而在单一内核中,每一个平面地址存储位数为16bit,四片即64bit,那512bit/64bit=8,即一个200MHz周期的512bit数据写入了8个平面地址,因此此处一次突发(即Wr_addr_cnt或者rd_addr_cnt每计数一次),app_addr_begin自增8。
该颗粒平面地址空间有16+10+3=29bit,上一段的描述提到一次突发需要8个地址,那么满容量可以进行多少次突发?即2^29(满地址)/8=67108864,将TEST_LENGTH设置为32’d60000000,即写了60000000/67108864=90%的空间,这样可以满足容量测试的要求。
④结合状态机运行状态和MIG返回的指示信号为app接口信号赋值,此处应结合各信号含义和接口时序核准。
⑤用户判错逻辑,写入和读出的都是从0开始递增的数据,当出错时指示灯闪烁。

parameter  TEST_LENGTH = 32'd60000000;
//***********1.先写后读状态机state machine
parameter  IDLE  = 2'd0;           
parameter  WRITE = 2'd1;          
parameter  WAIT  = 2'd2;           
parameter  READ  = 2'd3;   
reg [511:0]my_512_data;
reg [25:0] wr_addr_cnt;
reg [25:0] rd_addr_cnt;
reg [1:0]  state;
always @(posedge ui_clk or negedge rst_n) begin
 if((~rst_n)||(error_flag)) begin 
     state    <= IDLE;          
     my_512_data <= 512'd0;     
     wr_addr_cnt  <= 26'd0;      
     rd_addr_cnt  <= 26'd0;       
     app_addr_begin<= 30'd0;         
 end
 else if(init_calib_complete)begin               //MIG IP核初始化完成
     case(state)
        IDLE:begin
            state    <= WRITE;
            my_512_data <= 512'd0;   
            wr_addr_cnt  <= 26'd0;     
            rd_addr_cnt  <= 26'd0;       
            app_addr_begin     <= 30'd0; 
        end
        WRITE:begin
            if((wr_addr_cnt == TEST_LENGTH-1) &&(app_rdy && app_wdf_rdy))
                state    <= WAIT;                  //写到设定的长度跳到等待状态
            else if(app_rdy && app_wdf_rdy)begin   //写条件满足
                my_512_data <= my_512_data + 1;  //写数据自增
                wr_addr_cnt  <= wr_addr_cnt + 1;   //写计数自增
                app_addr_begin<= app_addr_begin + 8;      //DDR3 地址自增
            end else begin          //写条件不满足,保持当前状态
                 my_512_data <= my_512_data;      
                 wr_addr_cnt  <= wr_addr_cnt;
                 app_addr_begin<= app_addr_begin; 
            end
        end
        WAIT:begin                                                 
            state   <= READ;                     //下一个时钟,跳到读状态
            rd_addr_cnt <= 26'd0;                //读地址复位
            app_addr_begin<= 30'd0;                //DDR3读从地址0
        end
        READ:begin                               //读到设定的地址长度    
            if((rd_addr_cnt == TEST_LENGTH -1 ) && app_rdy)
                state   <= IDLE;                   //则跳到空闲状态 
            else if(app_rdy)begin                  //若MIG已经准备就绪,则开始读
                rd_addr_cnt <= rd_addr_cnt + 1'd1; //用户地址每次加一
                app_addr_begin    <= app_addr_begin + 8;       //DDR3地址加8
            end else begin   //若MIG没准备好,则保持原
                rd_addr_cnt <= rd_addr_cnt;
                app_addr_begin    <= app_addr_begin; 
            end
        end
        default:begin
            state    <= IDLE;
            my_512_data  <= 512'd0;
            wr_addr_cnt  <= 26'd0;
            rd_addr_cnt  <= 26'd0;
            app_addr_begin <= 30'd0;
        end
     endcase
 end
end   
//********2.根据状态机与MIG指示信号为app信号赋值
assign app_en  =((state == WRITE && (app_rdy && app_wdf_rdy))||(state == READ && app_rdy)) ? 1'b1:1'b0;             
assign app_cmd =(state == READ) ? 3'd1 :3'd0;  
assign app_wdf_wren=(state == WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;
assign app_wdf_end =app_wdf_wren; 
assign app_addr    =app_addr_begin;
assign app_wdf_data=my_512_data;   
//****************3.用户判错逻辑
reg     [25:0]   rd_cnt;
wire             rst_n;     //复位,低有效
reg              error_flag;
parameter  L_TIME = 28'd200_000_000;
reg     [27:0]   led_cnt;    //led计数
wire             error;     //读写错误标记
assign rst_n = ~ui_clk_sync_rst;//&&myrst
always @(posedge ui_clk or negedge rst_n) begin
 if(~rst_n) 
    rd_cnt  <= 0;              //若计数到读写长度,且读有效,地址计数器则�?0
 else if(app_rd_data_valid&&(rd_cnt == TEST_LENGTH - 1))
    rd_cnt <= 0;              //其他条件只要读有效,每个时钟自增1
 else if (app_rd_data_valid)
    rd_cnt <= rd_cnt + 1;
end
//判断错误,读出数据应为计数递增数据
assign error = (app_rd_data_valid && (rd_cnt!=app_rd_data));
always @(posedge ui_clk or negedge rst_n) begin
if(~rst_n)
    led2<=0;
else if(rd_cnt==32'd50000000)
    led2<=1;
end 
always @(posedge ui_clk or negedge rst_n) begin
 if(~rst_n) 
     error_flag <= 0;
 else if(error)
     error_flag <= 1;
end
//读写测试正确,指示灯led1常亮,反之则闪烁
always @(posedge ui_clk or negedge rst_n) begin
  if((~rst_n) || (~init_calib_complete )) begin
     led_cnt <= 28'd0;
     led1 <= 1'b0;
 end
 else begin
     if(~error_flag)
         led1 <= 1'b1;
     else begin
         led_cnt <= led_cnt + 28'd1;
         if(led_cnt == L_TIME - 1'b1) begin
         led_cnt <= 25'd0;
         led1 <= ~led1;
         end
    end
end
end

4.程序结果分析

正常结果可以看到与上一节MIG IP给出的时序是一致的。
●写开始时序:
写开始时序
●写结束时序:
写结束时序
●读开始时序:
读开始时序
●读结束时序:
读结束时序
测试的结果最好通过读写一定量并且读写指示灯亮确认。当测试异常时,应按照DDR是否初始化成功、MIG app接口时序是否正确、MIG ip配置是否正确、状态机运行状态是否异常方面入手分析。DDR初始化不成功init_calib_complete为0可能的原因是,时钟输入与MIG IP配置不一致,MIG端口没有正确连接,DDR的UCF约束有问题。

5.一个IP控制两颗DDR3

如下原理图,可以清楚的看到,两颗DDR3的相同的控制线连在了一个控制器的相同引脚,而数据线是各自连的。在PCB布线的时候通常用fly_by的方式连接DDR3。对于FPGA而言,把外部的位宽为16bit的两颗DDR当成了一个位宽为32bit的DDR3来控制。这种往往是为了扩大容量;或者为了匹配rank位宽;或者为了提升带宽,将两片或者四片甚至更多片DDR3放在一起。对于这种场景,只需将上一篇IP配置第5步中的data width设置为实际芯片加起来的位宽即可。如两片16bit位宽颗粒相连,则设置位宽为32bit即可。其余的用户逻辑且把他当作一片位宽为32bit的DDR3即可。实际上,用户存入的32bit数据在实际存放时,高位16bit和低位16bit的数据被放到不同的两片DDR3中,唯一的关联就是,这两个位置的物理值是相等的而已。
在这里插入图片描述

6.传送门

  • 我的主页
  • FPGA通信接口专栏汇总导航
  • 上一篇:MIG IP核的使用
  • DDR3测试工程
END

🔈文章原创,首发于CSDN论坛。
🔈欢迎点赞❤❤收藏⭐⭐打赏💴💴!
🔈欢迎评论区或私信指出错误❌,提出宝贵意见或疑问❓。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot的音乐翻唱与分享平台

基于SpringbootVue的音乐翻唱与分享平台设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 音乐资讯 音乐翻唱 在线听歌 后台登录 后台首页 用户管理 音乐资讯管理…

Elasticsearch:探索 11 种流行的机器学习算法

作者&#xff1a;来自 Elastic Elastic Platform Team 过去几年中&#xff0c;机器学习&#xff08;ML&#xff09;已经悄然成为我们日常生活中不可或缺的一部分。它影响着从购物网站和流媒体网站上的个性化推荐&#xff0c;到保护我们的收件箱免受我们每天收到的大量垃圾邮件的…

3.9设计模式——Strategy 策略模式(行为型)

意图 定义一系列的算法&#xff0c;把它们一个个封装起来&#xff0c;并且使他们可以相互替换此模式使得算法可以独立于使用它们的客户而变化 结构 Strategy&#xff08;策略&#xff09;定义所有支持的算法的公共入口。Context使用这个接口来调用某ConcreteStrategy定义的方…

手撕spring框架(2)

相关系列 java中spring底层核心原理解析&#xff08;1&#xff09;-CSDN博客 java中spring底层核心原理解析(2)-CSDN博客 手撕spring框架&#xff08;1&#xff09;-CSDN博客 依赖注入原理 依赖注入(Dependency Injection&#xff0c;简称DI)是一种设计模式&#xff0c;它允许我…

DS高阶:图论基础知识

一、图的基本概念及相关名词解释 1.1 图的基本概念 图是比线性表和树更为复杂且抽象的结&#xff0c;和以往所学结构不同的是图是一种表示型的结构&#xff0c;也就是说他更关注的是元素与元素之间的关系。下面进入正题。 图是由顶点集合及顶点间的关系组成的一种数据结构&…

深入浅出DBus-C++:Linux下的高效IPC通信

目录标题 1. DBus简介2. DBus-C的优势3. 安装DBus-C4. 使用DBus-C初始化和连接到DBus定义接口和方法发送和接收信号 5. dbus-cpp 0.9.0 的安装6. 创建一个 DBus 服务7. 客户端的实现8. 编译和运行你的应用9. 瑞芯微&#xff08;Rockchip&#xff09;的 Linux 系统通常会自带 db…

上位机开发PyQt(五)【Qt Designer】

PyQt5提供了一个可视化图形工具Qt Designer&#xff0c;文件名为designer.exe。如果在电脑上找不到&#xff0c;可以用如下命令进行安装&#xff1a; pip install PyQt5-tools 安装完毕后&#xff0c;可在如下目录找到此工具软件&#xff1a; %LOCALAPPDATA%\Programs\Python\…

智能体可靠性的革命性提升,揭秘知识工程领域的参考架构新篇章

引言&#xff1a;知识工程的演变与重要性 知识工程&#xff08;Knowledge Engineering&#xff0c;KE&#xff09;是一个涉及激发、捕获、概念化和形式化知识以用于信息系统的过程。自计算机科学和人工智能&#xff08;AI&#xff09;历史以来&#xff0c;知识工程的工作流程因…

【Web】2024XYCTF题解(全)

目录 ezhttp ezmd5 warm up ezMake ez?Make εZ?мKε? 我是一个复读机 牢牢记住&#xff0c;逝者为大 ezRCE ezPOP ezSerialize ezClass pharme 连连看到底是连连什么看 ezLFI login give me flag baby_unserialize ezhttp 访问./robots.txt 继…

ChatGPT向付费用户推“记忆”功能,可记住用户喜好 | 最新快讯

4月30日消息&#xff0c;人工智能巨头OpenAI宣布&#xff0c;其开发的聊天机器人ChatGPT将在除欧洲和韩国以外的市场全面上线“记忆”功能。这使得聊天机器人能够“记住”ChatGPT Plus付费订阅用户的详细信息&#xff0c;从而提供更个性化的服务。 OpenAI早在今年2月就已经宣布…

无缝对接配电自动化:IEC104转OPC UA网关解决方案

随着水电厂自动化发展的要求&#xff0c;具有一定规模的梯级水电站越来越多&#xff0c;为了实现水电站的无人值班(少人值守)&#xff0c;并考虑到节能控制&#xff0c;电厂采用了集中监控。集中监控关注的是整个电网的安全稳定运行及电压、频率和整个电网的电力需求&#xff0…

C++必修:类与对象(二)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. 构造函数 1.1. 定义 构造函数是一个特殊的成员函数&#xff0c;名字与类名相…

软件工程物联网方向嵌入式系统复习笔记--如何控制硬件

5-如何控制硬件 #mermaid-svg-of9KvkxJqwLjSYzH {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-of9KvkxJqwLjSYzH .error-icon{fill:#552222;}#mermaid-svg-of9KvkxJqwLjSYzH .error-text{fill:#552222;stroke:#552…

vue3步骤条带边框点击切换高亮

如果是div使用clip-path: polygon(0% 0%, 92% 0%, 100% 50%, 92% 100%, 0% 100%, 8% 50%);进行裁剪加边框没实现成功。目前这个使用svg完成带边框的。 形状可自行更改path 标签里的 :d“[num ! 1 ? ‘M 0 0 L 160 0 L 176 18 L 160 38 L 0 38 L 15.5 18 Z’ : ‘M 0,0 L 160,0…

Docker: 如何不新建容器 修改运行容器的端口

目录 一、修改容器的映射端口 二、解决方案 三、方案 一、修改容器的映射端口 项目需求修改容器的映射端口 二、解决方案 停止需要修改的容器 修改hostconfig.json文件 重启docker 服务 启动修改容器 三、方案 目前正在运行的容器 宿主机的3000 端口 映射 容器…

2024最新版JavaScript逆向爬虫教程-------基础篇之常用的编码与加密介绍(python和js实现)

目录 一、编码与加密原理1.1 ASCII 编码1.2 详解 Base641.2.1 Base64 的编码过程和计算方法1.2.2 基于编码的反爬虫设计1.2.3 Python自带base64模块实现base64编码解码类封装 1.3 MD5消息摘要算法1.3.1 MD5 介绍1.3.2 Python实现md5以及其他常用消息摘要算法封装 1.4 对称加密与…

【GitHub】github学生认证,在vscode中使用copilot的教程

github学生认证并使用copilot教程 写在最前面一.注册github账号1.1、注册1.2、完善你的profile 二、Github 学生认证注意事项&#xff1a;不完善的说明 三、Copilot四、在 Visual Studio Code 中安装 GitHub Copilot 扩展4.1 安装 Copilot 插件4.2 配置 Copilot 插件&#xff0…

Java设计模式 _结构型模式_组合模式

一、组合模式 1、组合模式 组合模式&#xff08;Composite Pattern&#xff09;是这一种结构型设计模式。又叫部分整体模式。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次关系。即&#xff1a;创建了一个包含自己对象组的类&#xff0c;该类提供了修改…

Educational Codeforces Round 165 (Rated for Div. 2)[A~D]

这场签到很快那会rank1400吧&#xff0c;但到c就写不动了&#xff0c;最后排名也是3000 左右&#xff0c;可见很多人其实都不会写dp。快速签到也很重要啊&#xff01;&#xff01; A. Two Friends Problem - A - Codeforces 题目大意&#xff1a; M有n个朋友&#xff0c;编号…

【Java】java实现文件上传和下载(上传到指定路径/数据库/minio)

目录 上传到指定路径 一、代码层级结构 二、文件上传接口 三、使用postman进行测试&#xff1b; MultipartFile接收前端传递的文件&#xff1a;127.0.0.1:8082/path/uploadFile part接收前端传递的文件&#xff1a;127.0.0.1:8082/path/uploadFileByRequest 接收前端传递…
最新文章