『FPGA通信接口』DDR(4)DDR3内存条SODIMMs读写测试

在这里插入图片描述

文章目录

  • 前言
  • 1.MIG IP核配置
  • 2.测试程序
  • 3.DDR应用
  • 4.传送门

前言

不论是DDR3颗粒还是DDR3内存条,xilinx都是通过MIG IP核实现FPGA与DDR的读写。本文区别于DDR颗粒,记录几个与颗粒配置不同的地方。关于DDR的原理与MIG IP的简介,请查看前面文章,链接在文末。本文提供了配套的工程源码,链接在文末,本文用的内存条为MT16KTF1G64HZ-1G6,FPGA芯片为xc7k325tffg900 -2。请按照顺序循序渐进阅读本系列的文章。

1.MIG IP核配置

  • 1.如无特殊说明,配置保持与前述文章MIG IP核配置一致。第五页如图所示配置。其中①指DDR3颗粒的物理时钟,例如一颗16bit位宽的DDR设置为400MHz,则它的传输速度为每一个800MHz(DDR双沿传输的原因)周期传输16bit。这里时钟的范围受到FPGA芯片速度等级和型号的制约,以及与内存条的支持速度也有关。如我用的内存条MT16KTF1G64HZ-1G6,速度范围在1500ps-3000ps之间。②指的是用户时钟,4:1的4指的是①设置的物理时钟。第③部分指的是类型选择,内存条选择SODIMMs。④处是DDR3内存条的型号,如果不在列表需要根据速度参数,位宽大小找一个兼容的型号。⑤处勾选上mask后,如果相应的管脚不连接,会造成DDR3初始化失败。其余配置保持默认即可。
    在这里插入图片描述
  • 2.第六页 此处①代表参考时钟,选择200MHz为固定大小。②处如果是内存条就选择RZQ/4,如果是颗粒就选择RZQ/6。
    在这里插入图片描述
  • 3.DDR3颗粒要勾选DCI Cascade,内存条不用勾选。后面就是选择引脚,其他的都保持默认,即可。
    在这里插入图片描述

2.测试程序

如果需要修改这段程序,需要注意MIG的接口各信号的位宽应该保持一致,另外程序中设计了两个LED灯,读写测试正确的时候,指示灯led1常亮,反之则闪烁。LED2只是容量,当测试到所设置的容量的时候常量。代码中TEST_LENGTH指示的含义是突发次数,也可以说是容量。每一次突发是512bit数据,使用内存的容量除以512bit,即为最大的突发次数。当然使用的芯片的物理位宽不同,例如只有一片16bit位宽的DDR颗粒,那一次突发的数据为8*16bit=128bit,那最大的突发次数就要用量除以128bit了。

  module dimm_top(
    input              sys_clk_p,
    input              sys_clk_n,       
  inout  [63:0]      ddr3_dq			, 
  inout  [7:0]       ddr3_dqs_n		, 
  inout  [7:0]       ddr3_dqs_p		,   
  output [15:0]      ddr3_addr		, 
  output [2:0]       ddr3_ba			, 
  output             ddr3_ras_n		, 
  output             ddr3_cas_n		, 
  output             ddr3_we_n		, 
  output             ddr3_reset_n	, 
  output [1:0]       ddr3_ck_p		, 
  output [1:0]       ddr3_ck_n		, 
  output [1:0]       ddr3_cke		    , 
  output [1:0]       ddr3_cs_n		, 
  output [7:0]       ddr3_dm			, 
  output [1:0]       ddr3_odt         , 
    output reg         led1,              
    output reg         led2        
    );         
    wire                clk_rst;                            
    wire                clk_200;
    reg     [29:0]      app_addr_begin=0;
    wire                app_en;              //写命令使能
    wire    [2:0]       app_cmd;             //用户读写命令
    wire                app_wdf_wren;        //DDR3写使能
    wire                app_wdf_end;         //突发写最后一个数标识
    wire    [29:0]      app_addr;            //用户平面地址
    wire                app_rdy;             //设备接收准备就绪   
    wire                app_wdf_rdy;         //写响应
    wire    [511:0]     app_rd_data;         //用户读数据
    wire                app_rd_data_end;     //突发读当前时钟最后一个数据
    wire                app_rd_data_valid;   //读数据有效
    wire    [511:0]     app_wdf_data;        //用户写数据
    wire                app_sr_active;       //保留
    wire                app_ref_ack;         //刷新请求
    wire                app_zq_ack;          //ZQ 校准请求
    wire        		init_calib_complete; //校准完成信号
    wire                ui_clk ;             //用户时钟
    wire                ui_clk_sync_rst;              
    clk_wiz_0 u_clk_wiz_0(.clk_out1(clk_200), .reset(1'b0), .locked(clk_rst), .clk_in1_p(sys_clk_p),.clk_in1_n(sys_clk_n));
  mig_7series_0 mig_JC (
    // Memory interface ports
    .ddr3_addr                      (ddr3_addr), 		// output [15:0]	
    .ddr3_ba                        (ddr3_ba),  		// output [2:0]
    .ddr3_cas_n                     (ddr3_cas_n), 		// output			
    .ddr3_ck_n                      (ddr3_ck_n),  		// output [1:0]		
    .ddr3_ck_p                      (ddr3_ck_p),  		// output [1:0]		
    .ddr3_cke                       (ddr3_cke),  		// output [1:0]		
    .ddr3_ras_n                     (ddr3_ras_n),  		// output		
    .ddr3_reset_n                   (ddr3_reset_n),  	// output			
    .ddr3_we_n                      (ddr3_we_n),  		// output		
    .ddr3_dq                        (ddr3_dq),  		// inout [63:0]	
    .ddr3_dqs_n                     (ddr3_dqs_n),  		// inout [7:0]		
    .ddr3_dqs_p                     (ddr3_dqs_p),  		// inout [7:0]		
    .init_calib_complete            (init_calib_complete),  // output		
  .ddr3_cs_n                      (ddr3_cs_n),  		// output [1:0]		
    .ddr3_dm                        (ddr3_dm),  		// output [7:0]	
    .ddr3_odt                       (ddr3_odt),  		// output [1:0]		
    // Application interface ports
    .app_addr                       (app_addr), 		 // input [29:0]		
    .app_cmd                        (app_cmd),  		 // input [2:0]		
    .app_en                         (app_en),  			 // input	
    .app_wdf_data                   (app_wdf_data), 	 // input [511:0]	
    .app_wdf_end                    (app_wdf_end), 		 // input			
    .app_wdf_wren                   (app_wdf_wren),  	 // input				
    .app_rd_data                    (app_rd_data),  	 // output [511:0]		
    .app_rd_data_end                (app_rd_data_end),   // output		
    .app_rd_data_valid              (app_rd_data_valid), // output	
    .app_rdy                        (app_rdy),  		 // output		
    .app_wdf_rdy                    (app_wdf_rdy),  	 // output			
    .app_sr_req                     (1'b0),  			 // input			
    .app_ref_req                    (1'b0),  			 // input		
    .app_zq_req                     (1'b0),              // input			
    .app_sr_active                  (app_sr_active),     // output			
    .app_ref_ack                    (app_ref_ack), 		 // output		
    .app_zq_ack                     (app_zq_ack),  	   	 // output		
    .ui_clk                         (ui_clk),  			 // output用户时钟输出,其实是通过IP配置自己配出来的 	 
    .ui_clk_sync_rst                (ui_clk_sync_rst),   // output		
    .app_wdf_mask                   (64'b0),  			 // input [63:0] //写数据屏蔽
    .sys_clk_i                      (clk_200),//输入IP的时钟
    // Reference Clock Ports
    .clk_ref_i                      (clk_200),//参考时钟		
    .sys_rst                        (clk_rst) // input sys_rst
    );
  parameter  TEST_LENGTH = 27'd134200000; //每一次突发是512bit 8GB可以支持134217728次突发 99.98%
  // 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 [26:0] wr_addr_cnt;
  reg [26: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  <= 27'd0;      
         rd_addr_cnt  <= 27'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  <= 27'd0;     
                rd_addr_cnt  <= 27'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 <= 27'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  <= 27'd0;
                rd_addr_cnt  <= 27'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     [26: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'd134200000-1)
        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
  endmodule

3.DDR应用

DDR在FPGA系统中的作用主要是作为存储器使用,用于存储数据和程序。DDR存储器通常被用作FPGA系统中的主存储器,用于存储采集数据和中间结果。DDR3作为高速缓存与FPGA相连,在不同领域均发挥着重要作用。在高性能计算领域,DDR用于存储大规模数据集、模型参数、数据计算结果,从而充分发挥FPGA并行计算的能力,完成计算任务;在图像处理领域,用于匹配图像采集接口与传输接口之间的速度,完成图像采集;在通信领域,DDR用于存储大量数据包,实现数据的缓存和处理,提高数据传输速度和处理效率。总之,DDR在不同领域扮演着重要角色。以采集摄像头数据为例,采用乒乓操作的思想是在DDR中开辟两块大小为1帧图像的缓冲区,如果读取速度大于写入速度的时候,需要采用乒乓操作的方式发挥DDR弹性缓冲的作用。往缓冲区写的时候,1号缓冲区写满之后,切换到2号缓冲区写,2号写满之后,在往1号去写,如此往复 。由于读取速度大于写入速度,因此,读一定是在与当前写不同的另一块缓冲区去读,在底层,可能会对同一块缓冲区的数据读取很多次,但是这并不影响人在视觉上对于画面流畅的影响。这就是通过乒乓操作实现了数据缓冲,匹配了读写两端的速度。如下图所示,
在这里插入图片描述
说明:1.每一个bank存储一帧图像数据,bank中每一行为图像的一行数据(即每一次读写的突发长度是一行像素数据,这个突发长度可以自己定义,并不必须要是一行数据),读和写彼此独立进行;2.bank之间的切换由状态机实现,由于读速度大于写速度,则每写完一个bank切换另一个bank去写;每读完一个bank,判断当前写bank,选择不同于写bank的bank进行读。3.由于读速度大于写速度,因此永远不会发生冲突,只是可能某一帧会被重复播放,但在视频应用中,这对用户的视觉不产生任何影响。同理,如果是读的速度小于写的速度,那让写操作刷新缓冲区即选择与读相排斥的缓冲区写,让读操作按照顺序读即可。 上面描述的只是一种乒乓缓冲的思想,实际操作中,可以通过设置三缓存,四缓存的方式让图像更为平滑。另外可以考虑基于这种思想为DDR写一个消息队列,让DDR仲裁控制器的通用性和适配性更强。

4.传送门

  • 我的主页
  • FPGA通信接口专栏汇总导航
  • DDR测试工程源码
  • 上一篇:DDR(3)DDR3颗粒读写测试
END

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

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

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

相关文章

Ts创建的详细过程及配置步骤(傻瓜式配置创建),最后效果展示

一:首先创建一个 空文件夹 二:使用编辑器打开,再创建一个src文件夹,然后按照以下步骤

批量文件重命名神器:以创建时间来命名,让你的文件井然有序!

在信息爆炸的时代&#xff0c;我们每天都在与无数的文件打交道。你是否曾为文件名的混乱而烦恼&#xff1f;是否曾为了快速找到某个文件而苦苦搜索&#xff1f;今天&#xff0c;我要为大家介绍一款神奇的工具——时光机&#xff0c;它能根据你的文件创建时间进行批量重命名&…

MBD、数字主线、MBSE、基于模型的企业等概念的比较分析

以产品研制全生命周期集成乃至新一步扩展为数字孪生为目的&#xff0c;目前发展的基于模型的定义&#xff08;MBD&#xff09;、数字主线&#xff08;DTh&#xff09;、基于模型的系统功能&#xff08;MBSE&#xff09;和基于模型的企业&#xff08;MBE&#xff09;等均有自身的…

一个肉夹馍思考的零耦合设计

刷抖音听说知识付费是普通人的一个收入增长点&#xff0c;写了三十几篇文章一毛钱没赚&#xff0c;感觉有点沮丧。天上下着小雨雨&#xff0c;稀稀嗦嗦的&#xff0c;由于了很久还是买了一个&#x1f928;。 忽然觉得生活有点悲催&#xff0c;现在已经变得斤斤计较&#xff0c;…

新手必看!场外个股期权的权利金估算公式

场外个股期权的权利金估算公式 场外个股期权的权利金估算公式通常涉及多个因素&#xff0c;这些因素共同决定了权利金的具体数额。虽然具体的估算公式可能因不同的交易平台、交易规则和标的资产而有所差异&#xff0c;但一般来说&#xff0c;权利金的计算会考虑以下几个关键要…

天软特色因子看板 (2024.4 第8期)

该因子看板跟踪天软特色因子A05005(近一月单笔流出金额占比(%)&#xff0c;该因子为近一月单笔流出金额占比(% 均值因子&#xff0c;用以刻画下跌时的 单成交中可能存在的抄底现象 今日为该因子跟踪第8期&#xff0c;跟踪其在SW801080 (申万电子) 中的表现&#xff0c;要点如下…

Java 对象创建过程十步法!你get到了吗?

Java 中对象的创建过程可以概括为十个步骤&#xff0c;从类加载到实例化对象。 下面详细讲解一下每个步骤&#xff1a; 1. 类加载&#xff1a; Java 虚拟机在加载类时&#xff0c;会检查类的字节码&#xff0c;并将其加载到内存中。类加载的过程包括加载、连接&#xff08;验…

Amine-PEG-Amine,956496-54-1在生物成像、生物传感器等领域具有广泛的应用

【试剂详情】 英文名称 Amine-PEG-Amine&#xff0c;NH2-PEG-NH2 中文名称 氨基-聚乙二醇-氨基&#xff0c;氨基PEG氨基&#xff0c; 双端氨基聚乙二醇 CAS号 956496-54-1 外观性状 由分子量决定&#xff0c;液体或者固体 分子量 0.4k&#xff0c;0.6k&#xff0c;1k&…

【JAVA |开篇】JAVA入门及JDK环境配置

目录 一、JIAVA语言 二、Java开发环境安装 三、初识Java的main方法 四、注释 一、JIAVA语言 Java 是一种优秀的程序设计语言 &#xff0c;它具有令人赏心悦目的语法和易于理解的语义 Write once, Run anywhere&#xff08;这句话体现了JAVA语言的核心&#xff0c;一次运行 任…

Vue从入门到精通-14-Vue组件

子组件的定义和注册 我们在本文的第一段中&#xff0c;通过Vue.component形式定义的是全局组件。这一段中&#xff0c;我们来讲一下子组件。 在父组件中定义子组件 比如说&#xff0c;一个账号模块是父组件&#xff0c;里面分为登陆模块和注册模块&#xff0c;这两个晓得模块…

ubuntu中的docker记录(3)——如何安装nvidia-docker以更好地支持GPU加速计算应用程序的运行

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、nvidia-docker2的安装1. 安装docker2. 安装nvidia-docker2(1) 添加密钥(2) 更新软件列表(3) 安装nvidia-docker2(4) 测试nvidia-docker2 二、可能的报错及解…

专家解读 | NIST网络安全框架(1):框架概览

随 着信息技术的快速发展&#xff0c;组织面临着越来越严峻的网络安全挑战。NIST网络安全框架&#xff08;NIST Cybersecurity Framework&#xff0c;CSF&#xff09;是一个灵活的综合性指南&#xff0c;旨在协助各类组织建立、改进和管理网络安全策略&#xff0c;以加强网络安…

leetCode81. 搜索旋转排序数组 II

leetCode81. 搜索旋转排序数组 II 题目思路 可以二分后的具体思路见我的上篇博客 搜索旋转排序数组 代码 class Solution { public:bool search(vector<int>& nums, int target) {if(nums.empty()) return false;int R nums.size() - 1;while(R > 0 &&…

LLMs:《Better Faster Large Language Models via Multi-token Prediction》翻译与解读

LLMs&#xff1a;《Better & Faster Large Language Models via Multi-token Prediction》翻译与解读 目录 《Better & Faster Large Language Models via Multi-token Prediction》翻译与解读 Abstract 2、Method方法 Memory-efficient implementation 高效内存实…

LabVIEW数据库访问技术

LabVIEW数据库访问技术 在当前的信息化时代&#xff0c;数据管理与分析在各个领域中起着重要的作用。特别是在工业、科研等领域&#xff0c;对于数据的快速准确获取、处理和分析需求日益增加。LabVIEW作为一种图形化编程语言&#xff0c;以其直观、高效的特点&#xff0c;在自…

【数据分析】这些年我发过的微信朋友圈

TencentRecordAnalysisV1.0.3.zip 蓝奏云&#xff1a;链接:链接TencentRecordAnalysis (lanzoub.com)密码:9hww 朋友圈还是以本行业岩土、工作相关的内容居多。 对于一个不怎么发圈的人来说&#xff0c;这几天有点反常&#xff0c;这几天大概是我成功的开发了几个失败的GPT应用…

打造亚马逊爆款秘诀:流量、排名与自养号测评的完美结合

亚马逊是一个产品为王的平台&#xff0c;只要我们的产品好&#xff0c;就会有更多的流量&#xff0c;有流量还怕我们的产品卖不出去&#xff1f;身为新手我们店无流量该怎么办&#xff0c;今天教给你们五个获取流量的方法。 1.自然检索 那是我们常说的自然流量&#xff0c;通…

DBCHM 数据库 CHM 文档生成工具

介绍 DBCHM 是一款数据库文档生成工具&#xff01; 该工具从最初支持chm文档格式开始&#xff0c;通过开源&#xff0c;集思广益&#xff0c;不断改进&#xff0c;又陆续支持word、excel、pdf、html、xml、markdown等文档格式的导出。 支持的数据库 SqlServerMySQLOraclePos…

拥抱新质生产力,助力新型工业化!CMM电子展暨IARS机器人展5月东莞盛大起航

2024年5月15-17日&#xff0c;东浩兰生会展集团旗下CMM电子展&#xff06;IARS机器人展将在广东现代国际展览中心&#xff08;东莞厚街&#xff09;举办。展会面积达50000平方米&#xff0c;展示品牌700余个&#xff0c;同期论坛峰会30余场&#xff0c;预计专业观众超50000人次…
最新文章