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

在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, // MHzparameter _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 refreg [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;endalways @(negedge clk) beginddr_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;endreg [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) begincs <= ns;endalways @(*) begincase (cs)0: beginif (initial_cnt == INITIAL_CYCLE) begin // wait 200usns = 1;end else beginns = 0;endend1: begin // wait 500us if (initial_stable_cnt == INITIAL_STABLE_CYCLE) begin // wait 500usns = 2;end else beginns = 1;endend2: beginns = 3;end3: beginif (freerun_cnt == FREE_CYCLE) beginif (initial_finish) beginns = 4;end else beginns = 2;endend else beginns = 3;endend4: begin// finish initial, enter idle stateenddefault: beginns = 0;endendcaseendalways @(negedge clk) begincase (ns)0: begininitial_cnt <= initial_cnt + 1;enddefault: begininitial_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)1: begininitial_stable_cnt <= initial_stable_cnt + 1;enddefault: begininitial_stable_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)3: beginfreerun_cnt <= freerun_cnt + 1;enddefault: beginfreerun_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)2: beginif (initial_cmd_ptr == 6 - 1) begininitial_finish <= 1;end else begininitial_finish <= 0;endinitial_cmd_ptr <= initial_cmd_ptr + 1;endendcaseendalways @(negedge clk) begincase (ns)0: beginddr_reset_n <= 1'b0;{ddr_cke_p2, ddr_cke_p1}    <= 2'b0;ddr_dqs_en_r <= 1'b0;ddr_dq_en_r  <= 1'b0;end1: beginddr_reset_n <= 1'b1;{ddr_cke_p2, ddr_cke_p1}    <= 2'b0;ddr_dqs_en_r <= 1'b0;ddr_dq_en_r <= 1'b0;end2: 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];end3: 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;enddefault: beginddr_reset_n <= 1'b1;endendcaseendendmodule

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

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

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

相关文章

Matlab:isomorphism

语法&#xff1a; P isomorphism(G1,G2) %计算图G1和G2之间的图同构等价关系&#xff08;如果存在&#xff09;。若不存在同构&#xff0c;则P为空数组 P isomorphism(___,Name,Value) %使用一个或多个名称-值对组参数指定其他选项 [P,edgeperm] isomorph…

Spark原理——Shuffle 过程

Shuffle 过程 Shuffle过程的组件结构 从整体视角上来看, Shuffle 发生在两个 Stage 之间, 一个 Stage 把数据计算好, 整理好, 等待另外一个 Stage 来拉取 放大视角, 会发现, 其实 Shuffle 发生在 Task 之间, 一个 Task 把数据整理好, 等待 Reducer 端的 Task 来拉取 如果更细…

Jenkins配置发邮件

Jenkins配置发邮件 账号设置 首先这个邮箱账号要支持发邮件&#xff0c;QQ邮箱开通SMTP即可之后要认证 企业微信邮箱 开启IMAP/SMTP服务开启POP/SMTP服务 无论是企业微信邮箱还是QQ邮箱都是SSL协议&#xff0c;在下面的配置中我都会勾选上&#xff01;&#xff01;&#xff0…

掌握WPF控件:熟练常用属性(一)

WPF布局常用控件&#xff08;一&#xff09; Border Border控件是一个装饰控件&#xff0c;用于围绕其他元素绘制边框和背景。它提供了一种简单的方式来为其他控件添加边框和背景样式&#xff0c;而无需自定义控件的绘制逻辑。 常用属性描述Background用于设置背景颜色或图像…

数据结构链表完整实现(负完整代码)

文章目录 前言引入1、链表定义及结构链表的分类3、单向不带头链表实现实现完整代码 4、带头双向循环链表实现实现完整代码 前言 引入 在上一篇文章中&#xff0c;我们认识了顺序表&#xff0c;但是在许多情况中&#xff0c;顺序表在处理一些事件时还存在许多问题&#xff0c;比…

【书生·浦语】大模型实战营——第四课笔记

教程链接&#xff1a;https://github.com/InternLM/tutorial/blob/main/xtuner/README.md 视频链接&#xff1a;https://www.bilibili.com/video/BV1yK4y1B75J/?vd_source5d94ee72ede352cb2dfc19e4694f7622 本次视频的内容分为以下四部分&#xff1a; 目录 微调简介 微调会使…

计算机体系结构----存储系统

本文严禁转载&#xff0c;仅供学习使用。参考资料来自中国科学院大学计算机体系结构课程PPT以及《Digital Design and Computer Architecture》、《超标量处理器设计》、同济大学张晨曦教授资料。如有侵权&#xff0c;联系本人修改。 1.1 引言 1.1.1虚拟和物理内存 程序员看到…

【科研技巧】如何判断某个期刊是什么类别及影响因子?是否是顶会?如何期刊内检索?AI写综述?AI做PPT?

相关链接 查找和免费下载文献的方式汇总国内外各大期刊关系、如何查看期刊等级以及查看某篇论文属于哪个期刊登录和访问EI(Engineering Village)数据库查找文献 1 如何判断某个期刊是什么类别及影响因子 https://sci.justscience.cn/ IFold是影响因子 期刊类别为SCIE、查看…

(收藏)数据治理:一文讲透数据安全

数据治理&#xff1a;一文讲透数据安全 数据安全是数据治理的核心内容之一&#xff0c;随着数据治理的深入&#xff0c;我不断的碰到数据安全中的金发姑娘问题&#xff08;指安全和效率的平衡&#xff09;。 DAMA说&#xff0c;降低风险和促进业务增长是数据安全活动的主要…

ssm基于JAVA的酒店客房管理系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本酒店客房管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

IT从业人员如何养生?

目前&#xff0c;电脑对人体生理和心理方面的负面影响已日益受到人们的重视。为此科学使用电脑&#xff0c;减少电脑和网络的危害是十分必要的。好代码网总结了一些it从业人员的保健知识&#xff0c;分享给大家。 一是要增强自我保健意识 工作间隙注意适当休息&#xff0c;一般…

构建中国人自己的私人GPT-有道GPT

创作不易&#xff0c;请大家多鼓励支持。 在现实生活中&#xff0c;很多人的资料是不愿意公布在互联网上的&#xff0c;但是我们又要使用人工智能的能力帮我们处理文件、做决策、执行命令那怎么办呢&#xff1f;于是我们构建自己或公司的私人GPT变得非常重要。 先看效果 一、…

JVM基础(7)——ParNew垃圾回收器

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

【LeetCode:30. 串联所有单词的子串 | 滑动窗口 + 哈希表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

【踩坑】flask_uploads报错cannot import name ‘secure_filename‘

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 背景说明 截至目前&#xff0c;用新版的flask实现文件上传(用到flask_uploads库)&#xff0c;会出现这个问题。 问题原因 版本问题&#xff0c;新的werkzeug已经把secure_filename的位置改了。 解决方法 手动修改…

绘制几何图形(Shape)

目录 1、创建绘制组件 2、形状视口viewport 3、自定义样式 4、场景示例 绘制组件用于在页面绘制图形&#xff0c;Shape组件是绘制组件的父组件&#xff0c;父组件中会描述所有绘制组件均支持的通用属性。具体用法请参考Shape。 1、创建绘制组件 绘制组件可以由以下两种形式…

【Python机器学习】分类器的不确定估计——预测概率

predict_proba的输出是每个类别的概率&#xff0c;通常比decision_function的输出更容易理解&#xff0c;对于二分类问题&#xff0c;它的形状始终是(n_samples,2)。 import mglearn.tools from sklearn.ensemble import GradientBoostingClassifier from sklearn.datasets im…

Vue-16、Vue列表渲染(v-for的使用)

1、vue遍历数组 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>列表渲染</title><script type"text/javascript" src"https://cdn.jsdelivr.net/npm/vue2/dist/vue.js"…

C#基础-空处理

在c#中&#xff0c;值对象是没有办法赋值为null的。比如说&#xff0c;你想要定义一个布尔值&#xff0c;你的赋值数据要么得是true、要么就得是false&#xff0c;默认情况下我们永远没可能给这个布尔赋值为null&#xff0c;即使只是对这个变量进行声明而不初始化数据&#xff…

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

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