DDR初始化 - 0

推荐链接:

  1. https://blog.csdn.net/m0_59161987/article/details/136760838
  2. https://www.cnblogs.com/biaohc/p/6346949.html
  3. https://f.daixianiu.cn/csdn/8588266265617235.html

程序目录结构

程序源码文件包含:

start.S:程序主函数

sdram_init.S:初始化SDRAM的函数

led_blink.c:led闪烁函数

Makefile和link.lds

概要

这段代码是三星出的demo程序,其中有些缺陷,要先看懂这块代码,然后按照前面文章的理解,手册上也给出了相关初始化的序列描述,在这些基础上我们对这个程序做修改,比如支持512M,1G的ddr配置等。

ddr初始化

DDR的初始化过程

S5PV210的用户手册里有对DDR初始化序列的描述:
   S5PV210有两个独立的DRAM控制器,一个最大支持512MB,一个最大支持1024MB,但两个控制器必须支持相同类型的内存。

根据三星S5PV210文档可知,DDR2类型内存的初始化流程如下:

1、提供稳压电源给内存控制器和内存芯片,内存控制器必须保持CLE在低电平,此时就会提供稳压电源。注:当CKE引脚为低电平时,XDDR2SEL应该处于高电平(PCB上用上拉电阻实现)

    2、根据时钟频率正确配置PhyControl0.ctrl_start_point和PhyControl0.ctrl_inc的值。配置的PhyControl0.ctrl_dll_on值为’1’以打开PHY DLL。

    3、数据选取脉DQS清除:依照时钟频率和内存的tAC参数正确设置PhyControl1.ctrl_shiftc和PhyControl1.ctrl_offsetcbit位的值。

    4、设置PhyControl0.ctrl_start位的值为’1’

    5、设置ConControl,同时关闭auto refresh自动刷新计数器

    6、设置MemControl,同时关闭所有的power down(休眠模式)。

    7、设置MemConfig0寄存器。如果有两组内存芯片(比如有8片DDR,这8片DDR是分别挂在Memory Port1和Memory Port2上),再配置MemConfig1寄存器。

    8、设置PrechConfig和PwrdnConfig寄存器

    9、根据内存的tAC参数设置TimingAref,TimingRow, TimingData和TimingPower寄存器

    10、如果需要QoS标准,配置QosControl0—15和QosConfig0-15寄存器

    11、等待PhyStatus0.ctrl_locked位变为’1’。检查是否PHY DLL是否已锁

    12、PHY DLL补偿在内存操作时由PVT(Process, Voltage and Temperature,处理器、电压和温度)变化引起的延迟量。但是,PHY DLL不能因某些可靠的内存操作而切断,除非是工作在低频率下。如果关闭PHY DLL,依照PhyStatus0.ctrl_lock_value[9:2]位的值正确配置PhyControl0.ctrl_force位的值来弥补延迟量(fix delay amount)。清除PhyControl0.ctrl_dll_on位的值来关闭PHY DLL。

    13、上电后,确定最小值为200us的稳定时钟是否发出

    14、使用DirectCmd寄存器发出一个NOP命令,保证CKE引脚为高电平

    15、等最小400ns

    16、使用DirectCmd寄存器发出一个PALL命令 

    17、使用DirectCmd寄存器发出一个EMRS2命令,program操作参数 

    18、使用DirectCmd寄存器发出一个EMRS3命令,program操作参数 

    19、使用DirectCmd寄存器发出一个EMRS命令来使能内存DLLs 

    20、使用DirectCmd寄存器发出一个MRS命令,重启内存DLL 

    21、使用DirectCmd寄存器发出一个PALL命令 

    22、使用DirectCmd寄存器发出两个Auto Refresh(自动刷新)命令 

    23、使用DirectCmd寄存器发出一个MRS命令,program操作参数,不要重启内存DLL 

    24、等待最小200时钟周期 

    25、使用DirectCmd寄存器发出一个EMRS命令给程序的运行参数。如果OCD校正(Off-Chip Driver,片外驱动调校)没有使用,改善一个EMRS命令去设置OCD校准的默认值。在此之后,发送一个EMRS指令去退出OCD校准模式,继续program操作参数 

    26、如果有两组DDR芯片,重复14-25步配置chip1的内存,刚刚配置的是chip0,也就是第一组内存芯片 

    27、配置ConControlto来打开自动刷新计数器 

    28、如果需要power down(休眠)模式,配置MemControl寄存器。

初始化过程需要28个小步骤,下面的代码,将28个小步骤划分为4个大部分:

  1. IO端口驱动强度设置
  2. 初始化PHY DLL
  3. 初始化DMC0
  4. 初始化DDR2 DRAM

初始化PHY DLL

下图是控制器内部结构的topview,可以从中窥探一下DLL的位置和作用:

在这里插入图片描述

上图解析:
64bit的AXI Channel的左边是CPU,右边就是DMC内部结构。控制命令和数据都是通过AXI总线来与DMC交互,可以看到数据和命令都有相应的缓存和fifo,这是因为CPU的速率非常高,控制器需要buffer将他们存起来。
Queue Arbiter是队列仲裁器,对已缓存的bank访问命令进行重排优化
AREF Cmd负责自刷新,每隔64MS发送自刷新命令。
CPU需要直接对DMC发布命令如MRS/EMRS,上下电命令,DMC还有一条APB总线从CPU连接到Functional Register,将命令送入direct cmd
经过final arbiter对所有命令和数据进行仲裁
latency control是一个很重要的部件,用来控制前面章节解释的所有t延时参数,如CL,AL延时。输入来自timing & configuration information

命令和时间延迟都有了,那么进入phy interface的都是理想波形,需要在经过DLL的处理才能送到PCB上去。
对于低速总线来说,不需要DLL。高速总线上的时序经过PCB以及device自身的部件处理,会有较大的延迟,一般都需要对PCB上信号再经过一定的“变形”才能送到PHY
DLL就是负责对返回信号做“变形”(一般用在read场景)

对physic部分的配置,也就是DLL配置,非常重要,决定了通信成败。DMC和Device都有DLL,但是功能和原理大相径庭。Device下的DLL比较简单,而DMC的DLL更加复杂所以需要详细配置。

device端DLL

在这里插入图片描述

上图是不带DLL的时序模型,图中DQ是数据线,而DQS是数据锁存信号。可以看到CLK上升沿和DQS上升沿之间(理论上要在DQS上升沿发送,下降沿接收)有一个D参数,这是由于信号在PCB上传输,经过device处理,所造成的延时。

图中还有一个AL延迟,这是由于数据线一共有很多根,在DQS采集时并不是所有数据线都已经稳定有效了,所以还需要一个AL延迟去做等待。

实际总的时延是ideally, “these two edges would be aligned”那块的延迟。为了尽量消除这里的时延,需要用到DLL来做调整:

在这里插入图片描述
上图是加入DLL之后的时序,DQS被向后延迟,一个full时钟周期,直到与CLK同步,这样DQ可以大致地与CLK对齐。

device的DLL比较简单,硬件自动帮我们做好了。我们只需要知道有这么个原理。

DMC端DLL

read情况下的DLL如何工作?

在这里插入图片描述

我们要在DQ的1/4处对数据进行采集,所以原来的DQS必须要经过90°的相位偏移。

接收读取命令的存储设备在读取延迟(即CAS延迟)后将数据发送到控制器。清除DQS后,PHY使用PHY DLL将DQS相移90度。使用移位的DQS,PHY对读取数据进行采样,并将数据保存到位于PHY内部的读取数据输入FIFO中。然后,控制器在考虑读延迟和读取延迟的情况下,从PHY获取数据,然后将其发送到AXI读通道。

一种RL之后的额外时序:
图中红框中的delay来自phy以及board级别的输入延迟。

在这里插入图片描述

rd_fetch 必须设置为2,兼容慢速device

存在一个内部DLL,它允许它在读取延迟的确切数量之后发送数据。如果我们假设有最小的或没有板级/PHY的输入延迟,如果采样负极性(Q1, Q3采样),由于数据被保存到PHY读取数据输入FIFO中,控制器以“读取延迟+1(读取)”周期将读取数据发送到AXI读取通道。读取周期是使用ConControl设置的。rd_fetch位域。

write情况下的DLL如何工作?
DQS的输出应该延迟90°的相位偏移,由于device的内部模拟电路复杂度不能做很高,所以只能由DMC来设置DLL。

如何配置DLL?

根据手册由:

根据时钟频率正确配置PhyControl0.ctrl_start_point和PhyControl0.ctrl_inc的值。配置的PhyControl0.ctrl_dll_on值为’1’以打开PHY DLL。
数据选取脉DQS清除:依照时钟频率和内存的tAC参数正确设置PhyControl1.ctrl_shiftc和PhyControl1.ctrl_offsetcbit位的值。

实际是配置如下三个寄存器,以及其位域:
在这里插入图片描述

在这里插入图片描述

PhyControl0:
ctrl_start是激活
ctrl_dll_on值为是总开关
ctrl_start_point是相位偏移起始点,手册建议0x10
ctrl_inc是每次相位偏移的size,手册建议0x10
因为DLL是模拟电路,不能像数字电路那样立即配置即生效。要经过反馈电路慢慢调节,所以需要配置inc和start_point.

PhyControl1:
ctrl_shiftc:粗调,DQS Cleaing,手册建议配置0x110,即T/2
ctrl_offsetc:细调,一般配置为0,不需要细调。

由于在DMC发送命令或数据,到memory device,再到memory device给出DMC反馈,这一路上是有各种延迟的,如I/O延迟,Bonding延迟,PKG延迟,Board延迟。为了知晓这一路延迟花了多少时间,需要有一个DQS Cleaning电路。
存在一个经验值,认为CK和DQS/DQ之间,有一个T/2的延迟,手册推荐也是配置T/2。

代码以及注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

#include "s5pv210.h"

// MemControlBL=4, 1Chip, DDR2 Type, dynamic self refresh, force precharge, dynamic power down off

#define DMC0_MEMCONTROL0x00202400

// MemConfig0256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed

#define DMC0_MEMCONFIG_00x20F00313

// MemConfig1

#define DMC0_MEMCONFIG_10x00F00313

 

// TimingAref   7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E) 200MHZ=0x618

#define DMC0_TIMINGA_REF        0x00000618

// TimingRow    for @200MHz

#define DMC0_TIMING_ROW         0x2B34438A

// TimingData   CL=3

#define DMC0_TIMING_DATA        0x24240000

// TimingPower

#define DMC0_TIMING_PWR         0x0BDC0343      

 

.global sdram_init

 

sdram_init:

//IO端口驱动强度设置,对DMC所使用的IO端口进行驱动能力配置。
/*
这个参数用来控制内存数据总线的信号强度,数值越高代表信号强度越高,增加信号强度可以提高超频的稳定性。
但是并非信号强度高就一定好。
*/

// 1. 设置DMC0 Drive Strength (Setting 2X)

ldrr0, =ELFIN_GPIO_BASE

ldrr1, =0x0000AAAA

strr1, [r0, #MP1_0DRV_SR_OFFSET]

ldrr1, =0x0000AAAA

strr1, [r0, #MP1_1DRV_SR_OFFSET]

ldrr1, =0x0000AAAA

strr1, [r0, #MP1_2DRV_SR_OFFSET]

ldrr1, =0x0000AAAA

strr1, [r0, #MP1_3DRV_SR_OFFSET]

ldrr1, =0x0000AAAA

strr1, [r0, #MP1_4DRV_SR_OFFSET]

ldrr1, =0x0000AAAA

strr1, [r0, #MP1_5DRV_SR_OFFSET]

ldrr1, =0x0000AAAA

strr1, [r0, #MP1_6DRV_SR_OFFSET]

ldrr1, =0x0000AAAA

strr1, [r0, #MP1_7DRV_SR_OFFSET]

ldrr1, =0x00002AAA

strr1, [r0, #MP1_8DRV_SR_OFFSET]



// 2. 初始化PHY DLL, Device和DMC之间的DLL都要配置

ldrr0, =APB_DMC_0_BASE

//step 3: PhyControl0 DLL parameter setting, manual 0x00101000

ldrr1, =0x00101000

strr1, [r0, #DMC_PHYCONTROL0]

//PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case

ldrr1, =0x00000086

strr1, [r0, #DMC_PHYCONTROL1]



//step 2: PhyControl0 DLL on

ldrr1, =0x00101002

strr1, [r0, #DMC_PHYCONTROL0]

//step 4: PhyControl0 DLL start

ldrr1, =0x00101003

strr1, [r0, #DMC_PHYCONTROL0]

 

find_lock_val:

//Loop until DLL is locked

ldrr1, [r0, #DMC_PHYSTATUS]

andr2, r1, #0x7

cmpr2, #0x7

bnefind_lock_val

 

//Force Value locking

andr1, #0x3fc0

movr2, r1, LSL #18

orrr2, r2, #0x100000

orrr2 ,r2, #0x1000

orrr1, r2, #0x3

strr1, [r0, #DMC_PHYCONTROL0]

 

// 3. 初始化DMC0

//step 5: ConControl auto refresh off

ldrr1, =0x0FFF2010

strr1, [r0, #DMC_CONCONTROL]

//step 6: MemControl BL=4, 1 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off

ldrr1, =DMC0_MEMCONTROL

strr1, [r0, #DMC_MEMCONTROL]

//step 7: MemConfig0 256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed

ldrr1, =DMC0_MEMCONFIG_0

strr1, [r0, #DMC_MEMCONFIG0]

//MemConfig1

ldrr1, =DMC0_MEMCONFIG_1

strr1, [r0, #DMC_MEMCONFIG1]

//step 8:PrechConfig

ldrr1, =0xFF000000

strr1, [r0, #DMC_PRECHCONFIG]

//step 9:TimingAref7.8us//133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)

ldrr1, =DMC0_TIMINGA_REF

strr1, [r0, #DMC_TIMINGAREF]

//TimingRowfor //200MHz

ldrr1, =DMC0_TIMING_ROW

strr1, [r0, #DMC_TIMINGROW]

//TimingDataCL=4

ldrr1, =DMC0_TIMING_DATA

strr1, [r0, #DMC_TIMINGDATA]

//TimingPower

ldrr1, =DMC0_TIMING_PWR

strr1, [r0, #DMC_TIMINGPOWER]

 

// 4. 初始化DDR2 DRAM DIRECTCMD的配置,像device写命令

//DirectCmdchip0 Deselect

ldrr1, =0x07000000//NOP

strr1, [r0, #DMC_DIRECTCMD]

//step 16:DirectCmdchip0 PALL

ldrr1, =0x01000000//

strr1, [r0, #DMC_DIRECTCMD]

//step 17:DirectCmdchip0 EMRS2

ldrr1, =0x00020000

strr1, [r0, #DMC_DIRECTCMD]

//step 18:DirectCmdchip0 EMRS3

ldrr1, =0x00030000

strr1, [r0, #DMC_DIRECTCMD]

//step 19:DirectCmdchip0 EMRS1 (MEM DLL on, DQS# disable)

ldrr1, =0x00010400

strr1, [r0, #DMC_DIRECTCMD]

//step 20:DirectCmdchip0 MRS (MEM DLL reset) CL=4, BL=4

ldrr1, =0x00000542

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip0 PALL

ldrr1, =0x01000000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip0 REFA

ldrr1, =0x05000000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip0 REFA

ldrr1, =0x05000000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip0 MRS (MEM DLL unreset)

ldrr1, =0x00000442

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip0 EMRS1 (OCD default)

ldrr1, =0x00010780

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip0 EMRS1 (OCD exit)

ldrr1, =0x00010400

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 Deselect

ldrr1, =0x07100000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 PALL

ldrr1, =0x01100000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 EMRS2

ldrr1, =0x00120000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 EMRS3

ldrr1, =0x00130000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 EMRS1 (MEM DLL on, DQS# disable)

ldrr1, =0x00110400

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 MRS (MEM DLL reset) CL=4, BL=4

ldrr1, =0x00100542

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 PALL

ldrr1, =0x01100000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 REFA

ldrr1, =0x05100000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 REFA

ldrr1, =0x05100000

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 MRS (MEM DLL unreset)

ldrr1, =0x00100442

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 EMRS1 (OCD default)

ldrr1, =0x00110780

strr1, [r0, #DMC_DIRECTCMD]

//DirectCmdchip1 EMRS1 (OCD exit)

ldrr1, =0x00110400

strr1, [r0, #DMC_DIRECTCMD]

//ConControlauto refresh on

ldrr1, =0x0FF02030

strr1, [r0, #DMC_CONCONTROL]

//PwrdnConfig

ldrr1, =0xFFFF00FF

strr1, [r0, #DMC_PWRDNCONFIG]

//MemControlBL=4, 1 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off

ldrr1, =0x00202400

strr1, [r0, #DMC_MEMCONTROL]

 

movpc, lr