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

在这里插入图片描述

前言

7系列的FPGA芯片不支持DDR4,使用DDR4需要更高性能的FPGA芯片,这里用到Kintex ultrascale+是支持DDR4的,具体FPGA芯片是XCKU3P-2FFVA676I,DDR4的颗粒为MT40A512M16LY- 075E时钟频率为750MHz-1333MHz,单颗容量为1GB,此处测试2个板贴DDR4颗粒,用到的还是MIG IP。与7系列MIG IP相比,IP的配置上有些许不同,但信号极其含义上基本一致。测试场景中,写入0-1000的数据,读出时计数,计数值与读出数据一致则认为没有错误,并用LED显示结果,没有错误的情况下,则认为读写测试通过。实际使用中,应该测试最大容量,最大速度的情况。MIG的接口信号与DDR3基本一致,参见之前文章,链接在文末。 需要注意IP核的复位信号DDR3的是低电平复位,DDR4是高电平复位.

1.IP核配置

第一页的配置如下图所示。其中,A处代表Mode and Interface:控制器的模式和接口选项,可以选择AXI4接口或者普通模式,并生成对应的PHY组件;B.DDR4IO上的时钟速率(周期),计算传输速率因为是DDR双沿传输要乘2。C.代表IO时钟与用户时钟的关系,选择4:1,代表用户时钟为250MHz,每1000ps要传输两个(DDR的原因)32bit(这里有两颗DDR4颗粒 162=32),而一个250MHz周期是4000ps,因此一个4000psDDR4有8次传输,这是为啥用户界面每次需读写8倍的数据宽度。这里两片DDR4要写328=256bit。D处选择参考时钟100MHz,这个具体看硬件设计,这里保持与硬件上的系统时钟一致。F处要选择对应的封装,颗粒就选component,G处选择相应的颗粒型号,如果列表中没有,就要在E处勾选,自己写好DDR4参数存到csv文件中进行导入,点击“here”会跳转到xilinx官网,下载响应模板,后文第二段介绍了其中部分参数的含义,供参考。I处会根据选择的不同时钟速率,实时调整,通常不需要配置,保持默认即可。I处clamshell topology当电路板的正面和反面都存在DDR4颗粒时(镜像对接),勾选此选项。选择此模式的原因是方便PCB布线(查看手册)。H处选择数据位宽,有颗粒物理位宽和数量决定,此处为16*2=32bit。首页中其他配置保持默认。
在这里插入图片描述

第二页,MIG可以生成时钟供其他模块使用。后面的配置按照默认即可。
在这里插入图片描述

2.测试程序

module ddr4_ctrl(       
     input                    ui_clk,                //用户时钟
     input                    ui_clk_sync_rst,       //复位,高有效
     input                    init_calib_complete,   //DDR3初始化完成
     input                    app_rdy,               //MIG 命令接收准备好标致
     input                    app_wdf_rdy,           //MIG数据接收准备好
     input                    app_rd_data_valid,     //读数据有效
     input          [255:0]   app_rd_data,           //用户读数据
     output reg     [28: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     [255:0]   app_wdf_data,          //用户写数据
     output reg     [1 :0]    state,                 //读写状态
     output reg     [23:0]    rd_addr_cnt,           //用户读地址计数
     output reg     [23:0]    wr_addr_cnt,           //用户写地址计数
     output reg     [20:0]    rd_cnt,                //实际读地址标记
     output reg               error_flag,            //读写错误标志
     output reg               led                    //读写测试结果指示灯
     );
 
 //parameter define
 parameter  TEST_LENGTH = 1000;
 parameter  L_TIME = 25'd25_000_000;
 parameter  IDLE        = 2'd0;
 parameter  WRITE       = 2'd1;          
 parameter  WAIT        = 2'd2;           
 parameter  READ        = 2'd3;            
 //reg define
 reg  [24:0]  led_cnt;    //led计数
 
 //wire define
 wire         error;     //读写错误标记
 wire         rst_n;     //复位,低有效
 
  //*****************************************************
 //**                    main code
 //***************************************************** 
 
 assign rst_n = ~ui_clk_sync_rst;
assign error = (app_rd_data_valid && (rd_cnt!=app_rd_data));//读信号有效,且读出的数不是写入的数时,将错误标志位拉高
 
 //在写状态MIG IP 命令接收和数据接收都准备好,或者在读状态命令接收准备好,此时拉高使能信号,
 assign app_en = ((state == WRITE && (app_rdy && app_wdf_rdy))
                 ||(state == READ && app_rdy)) ? 1'b1:1'b0;
 assign app_wdf_wren = (state == WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;//在写状态,命令接收和数据接收都准备好,此时拉高写使能
 assign app_wdf_end = app_wdf_wren;   //由于DDR3芯片时钟和用户时钟的分频选择4:1,突发长度为8,故两个信号相同
 assign app_cmd = (state == READ) ? 3'd1 :3'd0;   //处于读的时候命令值为1,其他时候命令值为0
     
 //DDR3读写逻辑实现
 always @(posedge ui_clk or negedge rst_n) begin
     if((~rst_n)||(error_flag)) begin 
         state    <= IDLE;          
         app_wdf_data <= 256'd0;     
         wr_addr_cnt  <= 24'd0;      
         rd_addr_cnt  <= 24'd0;       
         app_addr     <= 28'd0;          
     end
     else if(init_calib_complete)begin               //MIG IP核初始化完成
         case(state)
             IDLE:begin
                 state    <= WRITE;
                 app_wdf_data <= 256'd0;   
                 wr_addr_cnt  <= 24'd0;     
                 rd_addr_cnt  <= 24'd0;       
                 app_addr     <= 28'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   //写条件满足
                     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                                                  
                 state   <= READ;                     //下一个时钟,跳到读状态
                 rd_addr_cnt <= 24'd0;                //读地址复位
                 app_addr    <= 28'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    <= app_addr + 8;       //DDR3地址加8
                 end
                 else begin                             //若MIG没准备好,则保持原值
                     rd_addr_cnt <= rd_addr_cnt;
                     app_addr    <= app_addr; 
                 end
               end
             default:begin
                 state    <= IDLE;
                 app_wdf_data <= 256'd0;
                 wr_addr_cnt  <= 24'd0;
                 rd_addr_cnt  <= 24'd0;
                 app_addr     <= 28'd0;
             end
         endcase
     end
 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 == TEST_LENGTH - 1)
          rd_cnt <= 0;              //其他条件只要读有效,每个时钟自增1
     else if (app_rd_data_valid )
         rd_cnt <= rd_cnt + 1;
 end
 
 //寄存状态标志位
 always @(posedge ui_clk or negedge rst_n) begin
     if(~rst_n) 
         error_flag <= 0;
     else if(error)
         error_flag <= 1;
  end
  
 //led指示效果控制
 always @(posedge ui_clk or negedge rst_n) begin
      if((~rst_n) || (~init_calib_complete )) begin
         led_cnt <= 25'd0;
         led <= 1'b0;
     end
     else begin
         if(~error_flag)                        //读写测试正确         
             led <= 1'b1;                       //led灯常亮
          else begin                            //读写测试错误
             led_cnt <= led_cnt + 25'd1;
             if(led_cnt == L_TIME - 1'b1) begin
             led_cnt <= 25'd0;
             led <= ~led;                      //led灯闪烁
             end                    
          end
       end
 end

3.测试结果

在这里插入图片描述
在这里插入图片描述

4.自定义配置各字段含义

  • Part type:指内存条的形式,选Component即板贴颗粒的方式。
  • Part name:给新建配置起名字;
  • Rank,指的是DDR内存模块中的内存芯片组。每个Rank由一组内存芯片组成,它们在内存总线上共享同一个地址线。在DDR内存中,每个Rank可以被视为一个独立的逻辑单元,具有自己的存储容量和访问速度。在读取或写入数据时,内存控制器可以同时访问多个Rank,从而提高内存的带宽和性能。
  • StackHeight:DDR4的StackHeight指的是内存模块的堆叠高度,即内存芯片在垂直方向上的叠放数量。StackHeight为1和2表示了DDR4内存模块堆叠芯片的数量和布局方式;1时,表示内存模块的内存芯片只在一侧进行堆叠;2表示内存模块的内存芯片在两侧进行堆叠;
  • CA Mirror指"地址镜像"(Address Mirroring)技术。在DDR4内存中,每个DRAM芯片通常包含多个地址引脚,用于对内存地址进行编码和存储。地址镜像是指在DRAM芯片中重复使用地址引脚的技术,以便在减少引脚数量的同时,实现更高的内存容量。通常情况下,DDR4内存芯片包含两个地址引脚镜像,根据芯片制造商的设计,可以达到32个地址引脚的效果。这意味着采用地址镜像技术后,DRAM芯片可以存储更多的内存地址,从而实现更高的容量。DDR4 CA Mirror是指DDR4内存模块的命令/地址(Command/Address)引脚的布局方式。当DDR4 CA Mirror为0时,意味着命令和地址引脚的布局是非镜像的。而当DDR4 CA Mirror为1时,表示命令和地址引脚的布局是镜像的。在非镜像布局中,命令和地址引脚按照从左到右的顺序排列,符合标准的DDR4规范。这种布局通常用于大多数DDR4内存模块。而在镜像布局中,命令和地址引脚的顺序与非镜像布局相反,即从右到左的顺序排列。这种布局主要用于特定的DDR4内存模块,以满足特殊的设计要求或应用需求。
  • CS width:CS(Chip Select)宽度指的是内存模块中每个芯片选择信号的宽度,即每个芯片选择信号所占用的引脚数量。CS信号用于选择要进行读取或写入操作的内存芯片。
  • CKE width:CKE(Clock Enable)宽度指的是内存模块中时钟使能信号的宽度,即时钟使能信号所占用的引脚数量。CKE信号用于控制内存芯片的时钟输入。
  • ODT width:ODT(On-Die Termination)宽度指的是内存模块中内部终端电阻控制信号的宽度,即内部终端电阻控制信号所占用的引脚数量。ODT信号用于控制内存芯片的内部终端电阻,以匹配信号传输的阻抗。
  • CK width:CK(Clock)宽度指的是内存模块中时钟信号的宽度,即时钟信号所占用的引脚数量。
  • Memory speed grade:内存速度等级指的是内存模块的工作速度。它通常以数字表示,如DDR4-2400、DDR4-3200等,表示内存模块的最大数据传输速率。
  • Memory density:内存密度指的是内存模块的总容量,通常以GB(Gigabytes)为单位。
  • Component density:组件密度指的是每个内存芯片的容量,通常以Gb(Gigabits)为单位。
  • Memory device width:内存设备宽度指的是每个内存芯片的数据总线宽度,通常以位(bit)为单位。
  • Memory component width:内存组件宽度指的是每个内存芯片的数据总线宽度,通常以位(bit)为单位。
  • Data bits per strobe:每个数据脉冲的数据位数,表示内存模块数据传输效率。
  • IO Voltages:IO电压指的是内存模块的输入/输出电压
  • Data widths:数据宽度指的是内存模块支持的数据位数
  • Min period:最小周期,即内存模块支持的最小时钟周期。
  • Max period:最大周期,即内存模块支持的最大时钟周期。
  • tCKE:CKE信号的使能时间,表示CKE信号保持有效的时间。
  • tFAW:四个不同的bank之间的最小行激活时间。
  • tFAW_dlr:四个相邻bank之间的最小行激活时间。

5.传送门

END

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

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
FPGA(现场可编程门阵列)是一种可编程逻辑器件,它可以根据用户需求重新配置其内部逻辑电路,实现不同的功能。DDR4(双数据速率4)是一种高速动态随机存取存储器,用于计算机系统中的高性能数据存储。 在FPGA中使用DDR4进行读写操作是一种常见的需求。首先,我们需要将DDR4模块与FPGA进行连接,以便FPGA能够访问DDR4存储器。这通常通过调配器或控制器来实现,这些模块负责处理FPGADDR4之间的通信。 在进行DDR4读操作时,FPGA首先发送相应的读请求信号到DDR4控制器。控制器接收到请求后,会根据提供的读取地址和控制信号,将数据从DDR4存储器中读取出来,并通过数据总线返回给FPGAFPGA在接收到数据后,可以进一步进行处理或使用。 在进行DDR4写操作时,FPGA首先发送相应的写请求信号到DDR4控制器,并提供要写入的数据和写入地址。控制器接收到请求后,将数据写入到DDR4存储器中的指定地址位置。 为了保证DDR4读写操作的正确性和效率,还需要进行一些时序控制和信号同步工作。例如,FPGA需要发送适当的时钟信号来同步读写操作,以确保数据的稳定和一致性。此外,还需要进行读写延迟的优化,以提高DDR4读写的速度和性能。 总之,FPGADDR4的结合可以通过适当的连接和控制来实现读写操作。这种组合可以为数据处理和存储方面的应用提供高性能和灵活性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FPGA小油条

原创不易,请多支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值