【DDR】基于Verilog的DDR控制器的简单实现(一)——初始化

news2024/9/22 14:39:42

在FPGA中,大规模数据的存储常常会用到DDR。为了方便用户使用,Xilinx提供了DDR MIG IP核,用户能够通过AXI接口进行DDR的读写访问,然而MIG内部自动实现了许多环节,不利于用户深入理解DDR的底层逻辑。
公众号:FPGA奇男子

本文以美光(Micron)公司生产的DDR3芯片MT41J512M8RH-093为例,说明DDR芯片的操作过程。
在这里插入图片描述

该芯片的datasheet可以从厂商官网下载得到:(https://www.micron.com/-/media/client/global/documents/products/data-sheet/dram/ddr3/4gb_ddr3l.pdf?rev=305217e2f9bd4ef48d7c6f353dfc064c),这个datasheet包含了Micron公司多款DDR芯片,这里MT41J512M8RH-093芯片对应数据位宽×8,总容量4G(512M×8),频率2133(-093)的产品,在表格中需要注意区分,不同产品在时序参数上会有所区别。
在这里插入图片描述

DDR芯片的使用关键在于令接口的信号变化满足时序要求,在初始化过程中主要关注下面几个时序参数(来自P33 Table 9: Timing Parameters Used for IDD Measurements – Clock Units与P96 Table 59: Electrical Characteristics and AC Operating Conditions for Speed Extensions (Continued))

* CK(MIN)   0.938 ns
* CL        14 CK
* RCD(MIN)  14 CK
* RC(MIN)   50 CK
* RAS(MIN)  36 CK
* RP(MIN)   14 CK
* FAW       27 CK
* RRD       6  CK
* RFC       279CK
* XPR       > max(5CK, RFC+10ns)
* MRD       > 4 CK
* MOD       > max(12 CK, 15ns)
* ZQinit    < max(512nCK, 640ns)
* DLLK      > 512 CK

从datasheet的P12页的Fig2. Simplified State Diagram可以看到,DDR3芯片在上电(Power applied)后需要经过一系列的初始化步骤(主要包含三个部分Reset Procedure、Initialization、ZQ Calibration),之后进入正常工作状态(idle)。
在这里插入图片描述

上图中Command由多个信号的变化构成,在初始化过程主要用到以下几个指令。(来自P118 Table 70: Truth Table – Command)

* COMMAND     | NOP | MRS_1 | MRS_2 | MRS_3 | MRS_4 | ZQCL             
* ddr_cke     | 1 1 |  1 1  |  1 1  |  1 1  |  1 1  |  1 1   
* ddr_dqs_en  |  0  |   0   |   0   |   0   |   0   |   0    
* ddr_dq_en   |  0  |   0   |   0   |   0   |   0   |   0     
* ddr_cs_n    |  0  |   0   |   0   |   0   |   0   |   0     
* ddr_ras_n   |  1  |   0   |   0   |   0   |   0   |   1    
* ddr_cas_n   |  1  |   0   |   0   |   0   |   0   |   1    
* ddr_we_n    |  1  |   0   |   0   |   0   |   0   |   0    
* ddr_ba      | vvv |  010  |  011  |  001  |  000  |  000   
* ddr_addr    | vvv |   28  |   00  |   44  |  124  | a[10]=1      
* ddr_odt     |  0  |   0   |   0   |   0   |   0   |   0   

这里将时钟周期取为最小时钟周期0.938ns,对应时钟频率1066.099MHz。经过计算,Initialization阶段每条指令执行后的等待时间均不超过1us,因此这里将Initialization阶段每条指令执行后的等待时间均简化1us,最终得到的DDR3初始化代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: wjh776a68
// 
// Create Date: 01/05/2024 09:45:15 AM
// Design Name: micron_ddr
// Module Name: micron_ddr_init
// Project Name: micron_ddr
// Target Devices: vu9p
// Tool Versions: 2017.4
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

// ddr3 x8 4Gb MT41J512M8RH-093
/****************************
*  DDR3L-2133 https://www.micron.com/-/media/client/global/documents/products/data-sheet/dram/ddr3/4gb_ddr3l.pdf?rev=305217e2f9bd4ef48d7c6f353dfc064c
* CK(MIN) 0.938 ns
* CL        14 CK
* RCD(MIN)  14 CK
* RC(MIN)   50 CK
* RAS(MIN)  36 CK
* RP(MIN)   14 CK
* FAW       27 CK
* RRD       6  CK
* RFC       279CK
* XPR       >max(5CK, RFC+10ns)
* MRD       >4CK
* MOD       >max(12CK, 15ns)
* ZQinit    <max(512nCK, 640ns)
* DLLK      >512CK
* command all p118
* initial waveform p137
*
* COMMAND     | NOP | MRS_1 | MRS_2 | MRS_3 | MRS_4 | ZQCL             
* ddr_cke     | 1 1 |  1 1  |  1 1  |  1 1  |  1 1  |  1 1   
* ddr_dqs_en  |  0  |       |       |       |       |        
* ddr_dq_en   |  0  |       |       |       |       |         
* ddr_cs_n    |  0  |   0   |   0   |   0   |   0   |   0     
* ddr_ras_n   |  1  |   0   |   0   |   0   |   0   |   1    
* ddr_cas_n   |  1  |   0   |   0   |   0   |   0   |   1    
* ddr_we_n    |  1  |   0   |   0   |   0   |   0   |   0    
* ddr_ba      | vvv |  010  |  011  |  001  |  000  |         
* ddr_addr    | vvv |   28  |   00  |   44  |  124  |  a[10]      
* ddr_odt     |  0  |       |       |       |       |        
***************************************************************/
module micron_ddr_init #(
    parameter CLK_FREQ = 1066.099, // MHz
    parameter _1MS_CYCLE = 10.0**-3 / (1.0 / (CLK_FREQ * 10**6)),
    parameter _1US_CYCLE = 10.0**-6 / (1.0 / (CLK_FREQ * 10**6)),
    parameter integer INITIAL_CYCLE = 200 * _1US_CYCLE,
    parameter integer INITIAL_STABLE_CYCLE = 500 * _1US_CYCLE,
    parameter integer FREE_CYCLE = _1US_CYCLE
) (
    output  reg [15:0]  ddr_addr,
    output  reg [2:0]   ddr_ba,
    output  reg         ddr_cas_n,
    output  reg [0:0]   ddr_ck_n,
    output  reg [0:0]   ddr_ck_p,
    output  reg [0:0]   ddr_cke,
    output  reg [0:0]   ddr_cs_n,
    output  reg [0:0]   ddr_dm,
    inout       [7:0]   ddr_dq,
    inout       [0:0]   ddr_dqs_n,
    inout       [0:0]   ddr_dqs_p,
    output  reg [0:0]   ddr_odt,
    output  reg         ddr_ras_n,
    output  reg         ddr_reset_n,
    output  reg         ddr_we_n,

    input clk

);

localparam [27:0] NOP_CMD =  {11'b11000111000, 16'h0000, 1'b0};
localparam [27:0] MRS1_CMD = {11'b11000000010, 16'h0028, 1'b0};
localparam [27:0] MRS2_CMD = {11'b11000000011, 16'h0000, 1'b0};
localparam [27:0] MRS3_CMD = {11'b11000000001, 16'h0044, 1'b0};
localparam [27:0] MRS4_CMD = {11'b11000000000, 16'h0124, 1'b0};
localparam [27:0] ZQCL_CMD = {11'b11000110000, 16'h0400, 1'b0};

reg ddr_cke_p1, ddr_cke_p2;
reg ddr_dqs_i, ddr_dqs_o, ddr_dqs_en;
reg ddr_dq_i, ddr_dq_o, ddr_dq_en;

    OBUFDS OBUFDS_ck (
      .O(ddr_ck_p),   // 1-bit output: Diff_p output (connect directly to top-level port)
      .OB(ddr_ck_n), // 1-bit output: Diff_n output (connect directly to top-level port)
      .I(clk)    // 1-bit input: Buffer input
   );
   
   IOBUFDS #(
      .DQS_BIAS("FALSE")  // (FALSE, TRUE)
   )
   IOBUFDS_dqs_inst (
      .O(ddr_dqs_o),     // 1-bit output: Buffer output
      .I(ddr_dqs_i),     // 1-bit input: Buffer input
      .IO(ddr_dqs_p),   // 1-bit inout: Diff_p inout (connect directly to top-level port)
      .IOB(ddr_dqs_n), // 1-bit inout: Diff_n inout (connect directly to top-level port)
      .T(ddr_dqs_en)      // 1-bit input: 3-state enable input
   );
   
   IOBUF IOBUF_dq_inst (
     .O(ddr_dq_o),   // 1-bit output: Buffer output
     .I(ddr_dq_i),   // 1-bit input: Buffer input
     .IO(ddr_dq), // 1-bit inout: Buffer inout (connect directly to top-level port)
     .T(ddr_dq_en)    // 1-bit input: 3-state enable input
  );
  
  ODDRE1 #(
      .IS_C_INVERTED(1'b1),  // Optional inversion for C
      .IS_D1_INVERTED(1'b0), // Unsupported, do not use
      .IS_D2_INVERTED(1'b0), // Unsupported, do not use
      .SRVAL(1'b0)           // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)
   )
   ODDRE1_cke_inst (
      .Q(ddr_cke),   // 1-bit output: Data output to IOB
      .C(clk),   // 1-bit input: High-speed clock input
      .D1(ddr_cke_p1), // 1-bit input: Parallel data input 1
      .D2(ddr_cke_p2), // 1-bit input: Parallel data input 2
      .SR(1'b0)  // 1-bit input: Active High Async Reset
   );

//OBUFDS OBUFDS_dqs (
//      .O(ddr_dqs_p),   // 1-bit output: Diff_p output (connect directly to top-level port)
//      .OB(ddr_dqs_n), // 1-bit output: Diff_n output (connect directly to top-level port)
//      .I(ddr_dqs)    // 1-bit input: Buffer input
//   );

    reg [15:0]  ddr_addr_r;
    reg [2:0]   ddr_ba_r;
    reg         ddr_cas_n_r;
    reg [0:0]   ddr_cs_n_r;
    reg [0:0]   ddr_dm_r; // no ref
    reg [0:0]   ddr_odt_r;
    reg         ddr_ras_n_r;
    reg         ddr_we_n_r;
    reg         ddr_dq_en_r; 
    reg         ddr_dqs_en_r; 
    
   initial begin
       {ddr_cke_p2, ddr_cke_p1, ddr_dqs_en, ddr_dq_en, ddr_cs_n, ddr_ras_n, ddr_cas_n, ddr_we_n, ddr_ba, ddr_addr, ddr_odt} <= NOP_CMD;
       {ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= NOP_CMD;
   end

    always @(negedge clk) begin
        ddr_addr  <=  ddr_addr_r  ;
        ddr_ba      <=  ddr_ba_r    ;
        ddr_cas_n  <=  ddr_cas_n_r ;
        ddr_cs_n  <=  ddr_cs_n_r  ;
        ddr_dm      <=  ddr_dm_r    ;
        ddr_odt      <=  ddr_odt_r   ;
        ddr_ras_n  <=  ddr_ras_n_r ;
        ddr_we_n  <=  ddr_we_n_r  ;
        ddr_dq_en   <=  ddr_dq_en_r;
        ddr_dqs_en  <=  ddr_dqs_en_r;

    end


   reg [5:0] cs = 0, ns;
   reg [31:0]  initial_cnt = 0;
   reg [31:0]  initial_stable_cnt = 0;
   reg [31:0]  freerun_cnt = 0;
   reg [3:0]   initial_cmd_ptr = 0;
   reg [27:0] initial_cmd_seq[0:5] = '{NOP_CMD, MRS1_CMD, MRS2_CMD, MRS3_CMD, MRS4_CMD, ZQCL_CMD};
   reg initial_finish = 0;

   always @(negedge clk) begin
       cs <= ns;
   end

   always @(*) begin
       case (cs)
       0: begin
           if (initial_cnt == INITIAL_CYCLE) begin // wait 200us
               ns = 1;
           end else begin
               ns = 0;
           end
       end
       1: begin // wait 500us 
           if (initial_stable_cnt == INITIAL_STABLE_CYCLE) begin // wait 500us
               ns = 2;
           end else begin
               ns = 1;
           end
       end
       2: begin
           ns = 3;
       end
       3: begin
           if (freerun_cnt == FREE_CYCLE) begin
               if (initial_finish) begin
                   ns = 4;
               end else begin
                   ns = 2;
               end
           end else begin
               ns = 3;
           end
       end
       4: begin
           // finish initial, enter idle state
       end
       default: begin
           ns = 0;
       end
       endcase
   end

   always @(negedge clk) begin
       case (ns)
       0: begin
           initial_cnt <= initial_cnt + 1;
       end
       default: begin
           initial_cnt <= 0;
       end
       endcase
   end

   always @(negedge clk) begin
       case (ns)
       1: begin
           initial_stable_cnt <= initial_stable_cnt + 1;
       end
       default: begin
           initial_stable_cnt <= 0;
       end
       endcase
   end

   always @(negedge clk) begin
       case (ns)
       3: begin
           freerun_cnt <= freerun_cnt + 1;
       end
       default: begin
           freerun_cnt <= 0;
       end
       endcase
   end

   always @(negedge clk) begin
       case (ns)
       2: begin
           if (initial_cmd_ptr == 6 - 1) begin
               initial_finish <= 1;
           end else begin
               initial_finish <= 0;
           end
           initial_cmd_ptr <= initial_cmd_ptr + 1;
       end
       endcase
   end


   always @(negedge clk) begin
       case (ns)
       0: begin
           ddr_reset_n <= 1'b0;
           {ddr_cke_p2, ddr_cke_p1}    <= 2'b0;
           ddr_dqs_en_r <= 1'b0;
           ddr_dq_en_r  <= 1'b0;
       end
       1: begin
           ddr_reset_n <= 1'b1;
           {ddr_cke_p2, ddr_cke_p1}    <= 2'b0;
           ddr_dqs_en_r <= 1'b0;
           ddr_dq_en_r <= 1'b0;
       end
       2: begin
           {ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= initial_cmd_seq[initial_cmd_ptr];
       end
       3: begin
           {ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= NOP_CMD;
       end
       default: begin
           ddr_reset_n <= 1'b1;
       end
       endcase
   end


endmodule

上述代码已在Vivado 2017.4中进行了仿真测试,可替换ddr示例工程中的example_top自行仿真。
在这里插入图片描述
下一节 【DDR】基于Verilog的DDR控制器的简单实现(二)——写操作

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

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

相关文章

使用emu8086实现——顺序程序设计

一、实验目的 1. 掌握顺序程序设计方法 2. 掌握汇编语言编程设计方法。 二、实验内容 1.用查表的方法将一位十六进制数转换成它相应的ASCII码。 代码及注释&#xff1a; Data segment ;定义数据段Tab db 30h,31h,32h,33h,34h,35,36h,37h,38h,39h ;定义一个Tab的字节型…

云卷云舒:AI for DB、DB for AI

云卷云舒&#xff1a;算力网络云原生&#xff08;下&#xff09;&#xff1a;云数据库发展的新篇章-CSDN博客https://blog.csdn.net/bishenghua/article/details/135050556 随着数据库和AI技术的分支同向演进&#xff0c;AI 和数据库间的关联越发紧密了。 大模型的演进发展&a…

mybatisplus配置

一、新建项目&#xff1a;com.saas.plusdemo 二、配置pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sch…

借助Gitee将typora图片上传CSDN

概述 前面已经发了一个如何借助Github将typora上的图片上传到csdn上&#xff0c;但这有个缺陷&#xff1a;需要科学上网才能加速查看已经上传到github上的图片&#xff0c;否则就会出现已经上传的图片&#xff0c;无法正常查看的问题 如何解决&#xff1f; 那就可以使用Gite…

WEB 3D技术 three.js 聚光灯

本文 我们来说说 点光源和聚光灯 点光源 就像一个电灯泡一样 想四周发散光 而聚光灯就像手电筒一样 像一个方向射过去 距离越远范围越大 光越弱 我们先来看一个聚光灯的效果 我们可以编写代码如下 import ./style.css import * as THREE from "three"; import { O…

陪诊小程序|北京陪诊系统满足医疗陪诊需求的便捷工具

陪诊小程序是一种创新的线上医疗服务工具&#xff0c;旨在为用户提供便捷的医疗陪诊服务。通过陪诊小程序&#xff0c;用户可以轻松预约陪诊服务&#xff0c;选择合适的服务时间和医院科室&#xff0c;并了解服务详情和其他用户的评价。同时&#xff0c;陪诊员也可以利用小程序…

二、Sharding-JDBC系列02:自定义主键生成策略

目录 一、概述 二、自定义主键生成策略 (1)、自定义MyShardingKeyGenerator (2)、SPI接口配置 (3)、配置主键ID生成策略 (4)、测试数据插入 一、概述 实际应用中&#xff0c;大部分场景按照MySQL主键ID自增就能满足需求&#xff0c;但是在分库分表后&#xff0c;MySQL的…

弟12章 1 网络编程

文章目录 网络协议概述 p164TCP协议与UDP协议的区别 p165 网络协议概述 p164 ipv4&#xff1a;十进制点分制 ipv6&#xff1a;十六进制冒号分隔 TCP协议与UDP协议的区别 p165 tcp协议的三次握手&#xff1a;

双向冒泡排序的数据结构实验报告

目录 实验目的&#xff1a; 实验内容&#xff08;实验题目与说明&#xff09; 算法设计&#xff08;核心代码或全部代码&#xff09; 运行与测试&#xff08;测试数据和实验结果分析&#xff09; 总结与心得&#xff1a; 实验目的&#xff1a; 理解双向冒泡排序算法的原…

FreeRTOS 学习相关笔记 附C语言内容补充

学习视频为【正点原子】手把手教你学FreeRTOS实时系统 文章目录 RTOS 入门裸机RTOSFreeRTOS任务调度方式任务状态 FreeRTOS 移植系统配置文件 API函数任务创建和删除动态方式创建任务静态方式创建任务删除任务 任务挂起和恢复函数调度器挂起与恢复临界区任务调度器的挂起和恢复…

估算监控最低可以存储的时长

监控可以存储的时长&#xff0c;主要取决于码率&#xff0c;知道了码率就知道一天可以的视频产生多少视频数据。 以乐橙官网给出的计算&#xff0c;我们可以推出这个设备8MP本地的录像码率大概在4Mbps左右。 同样的我们这里附一张表格&#xff0c;大家可以根据这个来估算存储…

leetcode14. 最长公共前缀

题目 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 解题方法&#xff1a; 1.首先找到数组中长度最短的数据&#xff0c;与数组第一个数进行交换&#xff08;公共前缀的长度肯定不会大于列表中长度最短的字符串&#x…

大模型实战营Day3 作业

基础作业&#xff1a; 复现课程知识库助手搭建过程 (截图) 进阶作业&#xff1a; 选择一个垂直领域&#xff0c;收集该领域的专业资料构建专业知识库&#xff0c;并搭建专业问答助手&#xff0c;并在 OpenXLab 上成功部署&#xff08;截图&#xff0c;并提供应用地址&#xf…

pyenv虚拟环境安装和配合pipenv多版本创建

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、下载配置pyenv二、配置多版本虚拟环境总结 前言 最近公司编写了一个自动化用例编写软件&#xff0c;需要适配win7和win10系统&#xff0c;需要同时编译3.8…

Nacos和Eureka比较、统一配置管理、Nacos热更新、多环境配置共享、Nacos集群搭建步骤

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Nacos和eureka的对比二、统一配置管理二、Nacos热更新方式一方式二 三、多环境配置共享四、Nacos集群搭建步骤&#xff08;黑马springCloud的p29&#xff0…

如何实现接口重试

重试机制 在复杂的接口业务中&#xff0c;API请求数量很多&#xff0c;并且业务处理复杂&#xff0c;便难免会遇到一些网络问题(timeout)或者未知错误(error)&#xff0c;这时候需要加入重试机制了。让我们来回顾一下都有什么实现机制吧。 8种重试机制实现 1. 循环重试 这是最…

基于SSM+JSP的订餐管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

不方便拉网线,房间又没Wifi信号?按照这个教程,让你家里每个角落都有网

前言 前段时间去了一个朋友家里&#xff0c;她老是和我叨叨说她家的卧室一点Wifi信号都没有。每次一躺床上都只能用手机流量上网。 家里明明有拉宽带&#xff0c;为什么在某一些地方还是得用自己手机流量&#xff1f;哎&#xff0c;有钱人的痛就是房子太大了。 我问她为啥不多…

Open3D 从体素网格构建八叉树(14)

Open3D 从体素网格构建八叉树(14) 一、算法简介二、算法实现1.代码2.效果一、算法简介 上一章介绍从点云构建八叉树,对点云所在体素进行了可视化显示,这里可以对体素构建八叉树,可视化显示八叉树的具体划分结构。 二、算法实现 1.代码 代码如下(示例): import op…

微服务治理:微服务断路器(微服务故障隔离模式)详解

微服务断路器是一种设计模式&#xff0c;可以保护系统免于级联故障&#xff0c;通过限制对故障服务的调用来实现。它的工作原理类似于电气断路器&#xff1a;当服务遇到问题时&#xff0c;它会切断请求流&#xff0c;使其有机会恢复&#xff0c;并防止其他服务被压垮。 工作原…