前言:内存为九鼎 X210 开发板 512MB DDR2
1、SDRAM引入
1.1、SDRAM:Syncronized Dynamic Ramdam Access Memory,同步动态随机存储器。
DDR:就是DDR SDRAM,是SDRAM的升级版。(DDR:double rate,双倍速度的SDRAM)
DDR有好多代:DDR1 DDR2 DDR3 DDR4 LPDDR
1.2、SDRAM的特性(容量大、价格低、掉电易失性、随机读写、总线式访问)
SDRAM/DDR都属于动态内存(相对于静态内存SRAM),都需要运行一段初始化代码来初始化才能使用,不像SRAM开机上电后就可以直接使用。类似于SDRAM和SRAM的区别,还有Norflash和Nandflash(硬盘)这两个。正是因为硬件本身特性有限制,所以才导致启动代码比较怪异、比较复杂。而我们研究裸机就是为了研究uboot,在uboot中就充分利用了硬件的各种特性,处理了硬件复杂性。SDRAM在整个硬件系统中是属于外部设备,通过地址总线和数据总线接口与SoC通信
1.3、SDRAM数据手册
SDRAM在系统中属于SoC外接设备(外部外设。很多东西都逐渐集成到SoC里面去了。现在还长期在外部的一般有:flash、SDRAM/DDR、网卡芯片如DM9000、音频Codec。现在有一些高集成度的芯片也试图这几个集成进去,做成真正的单芯片解决方案。)
SDRAM通过地址总线和数据总线接口与SoC通信。
2、SDRAM初始化
2.1、原理图的SDRAM相关部分
S5PV210共有两个内存端口(就好像有两个内存插口)。查阅数据手册中内存映射部分,可知:两个内存端口分别叫DRAM0和DRAM1:
DRAM0:内存地址范围:0x20000000 ~ 0x3FFFFFFF(512MB),对应引脚是Xm1xxxx
DRAM1:内存地址范围:0x40000000 ~ 0x7FFFFFFF(1024MB),对应引脚是Xm2xxxx
结论:
(1)整个210最多支持内存为1.5GB,如果给210更多的内存,CPU就无法识别。
(2)但实际上开发板不一定要这门多,譬如开发板210就只有512MB内存,连接方法是在DRAM0端口分布256MB,在DRAM1端口分布256MB。
(3)210开发板上内存合法地址是:0x20000000 ~ 0x2FFFFFFF(256MB) + 0x40000000 ~ 0x4FFFFFFF(256MB)。当板子上DDR初始化完成之后,这些地址都是可以使用的;如果使用了其他地址譬如0X30004000就是死路一条。
原理图中每个DDR端口都由3类总线构成:地址总线(Xmn_ADDR0 ~ Xmn_ADDR13共14根地址线)+ 控制总线(中间部分)+ 数据总线(Xmn_DATA0 ~ Xmn_DATA31共32根数据线)
分析:从数据总线的位数可以看出,我们用的是32位的(物理)内存。
原理图中画出4片内存芯片的一页,可以看出:X210开发板共使用了4片内存(每片 128MB,共512MB),每片内存的数据总线都是16位的(单芯片是16位内存)。原理图上横向的两颗内存芯片就是并联连接得到32内存的。并联时地址总线接法一样,但是数据总线要加起来。这样连接相当于在逻辑上可以把这两颗内存芯片看成是一个(这一个芯片是32位的,接在Xm1端口上)。
2.2、数据手册中SDRAM相关部分
数据手册《NT5TU64M16GG-1G-G-R18-Consumer》第10页的block diagram。这个框图是128Mb * 8结构的,这里的8指的是8bank,每bank128Mbit。210的DDR端口信号中有BA0 ~ BA2,接在内存芯片的BA0 ~BA2上,这些引脚就是用来选择bank的。每个bank内部有128Mb,通过row address(14位) + column address(10位)的方式来综合寻址。一共能寻址的范围是:2的14次方 + 2的10次方 = 2的24次方。对应16MB(128Mbit)内存。
3、汇编初始化SDRAM
3.1、初始化代码框架介绍(函数调用和返回、步骤等)
初始化过程包括phy dll初始化、设置控制器寄存器和内存初始化。
SDRAM初始化使用一个函数sdram_asm_init,函数在sdram_init.S文件中实现,是一个汇编函数。
强调:汇编实现的函数在返回时需要明确使用返回指令(mov pc, lr)。
3.2、27步初始化DDR2
(1)首先,DDR初始化和SoC(准确地说是与SoC中的DDR控制器)有关,也和开发板上使用的DDR芯片有关,和开发板设计时DDR的连接方式也有关。
(2)S5PV210的DDR初始化步骤在SoC数据手册我:1.2.1.3DDR2这个这章节。可知初始化共有27个步骤。
(3)X210的内存连接方式是:在DRAM0上连接256MB,在DRAM1上连接了256MB。所以初始化时分为两部分,第一部分初始化DRAM0,第二部分出初始化DRAM1。
3.3、设置IO端口驱动强度
因为DDR芯片和S5PV210之间是通过很多总线连接的,总线的物理表现就是很多个引脚,也就是说DDR芯片和S5PV210是通过一些引脚连接的。DDR芯片工作时需要一定的驱动信号,这个驱动信号需要一定的点电平才能抗干扰,所以需要设置这些引脚的驱动能力,使DDR正常工作。
DRAM控制器对应的引脚设置为驱动强度2X。
3.4、DRAM port 时钟设置
主要是开启DLL,然后等待锁存。
3.5、DMC0_MEMCONTROL
burst length = 4,1chip,...... 对应的值是0x00202400
3.6、DMC0_MEMCONFIG_0
DRAM0通道中memory chip0的参数设置寄存器
3.7、DMC0_MEMCONFIG_1
DRAM0通道中memory chip1的参数设置寄存器
总结:三星设置DRAM0通道,允许我们接两片256MB的内存,分别叫memory chip0和memory chip1,分别用这两个寄存器来设置它的参数。按照三星的设计,chip0的地址应该是0x20000000到0x2FFFFFFF,然后chip1的地址应该是0x30000000 ~ 0x3FFFFFFF。各自256MB。但是我们开发板实际上在DRAM0端口只接了256MB的内存,所以只用了chip0,没有使用chip1。(虽然是两片内存芯片并联形成32位内存的,逻辑上只能算一片)。按照这个推论,DMC0_MEMCONFIG_0有用,而DMC0_MEMCONFIG_1没用,所以直接给它默认值。
3.8、DMC_DIRECTCMD
这个寄存器是个命令寄存器,我们210通过向这个寄存器写值来向DDR芯片发送命令(通过命令总线),这些命令应该都是用来配置DDR芯片的工作参数。
总结:DDR配置过程比较复杂,基本上是按照DDR控制器的时序要求来做的,其中很多参数要结合DDR芯片本身的参数来定,还有些参数是时序参数,要去详细计算。所以DDR配置非常繁琐、细致、专业。所以我们对DDR初始化的态度就是:学会这种思路和方法,结合文档和代码能看懂,会算一些常见的参数即可。
3.9、重定位代码到SDRAM中
DRAM初始化之后,实际上重定位代码过程和之前重定位到SRAM中完全相同。
4.SDRAM配置代码
#include "s5pv210.h"
#if 1
#define DMC0_MEMCONTROL 0x00202400
#define DMC0_MEMCONFIG_0 0x20F01323
#define DMC0_MEMCONFIG_1 0x30F00312 //MemConfig1默认值
#define DMC0_TIMINGA_REF 0x00000618
#define DMC0_TIMING_ROW 0x28233287
#define DMC0_TIMING_DATA 0x23240304
#define DMC0_TIMING_PWR 0x09C80232
#define DMC1_MEMCONTROL 0x00202400
#define DMC1_MEMCONFIG_0 0x40F01323
#define DMC1_MEMCONFIG_1 0x60E00312
#define DMC1_TIMINGA_REF 0x00000618
#define DMC1_TIMING_ROW 0x28233289
#define DMC1_TIMING_DATA 0x23240304
#define DMC1_TIMING_PWR 0x08280232
#endif
#if 0
#define DMC0_MEMCONTROL 0x00212400
#define DMC0_MEMCONFIG_0 0x20E01323
#define DMC0_MEMCONFIG_1 0x40F01323
#define DMC0_TIMINGA_REF 0x00000618
#define DMC0_TIMING_ROW 0x28233287
#define DMC0_TIMING_DATA 0x23240304
#define DMC0_TIMING_PWR 0x09C80232
#define DMC1_MEMCONTROL 0x00202400
#define DMC1_MEMCONFIG_0 0x40C01323
#define DMC1_MEMCONFIG_1 0x00E01323
#define DMC1_TIMINGA_REF 0x00000618
#define DMC1_TIMING_ROW 0x28233289
#define DMC1_TIMING_DATA 0x23240304
#define DMC1_TIMING_PWR 0x08280232
#endif
.global sdram_asm_init
sdram_asm_init:
ldr r0, =0xF1E00000
ldr r1, =0x00
str r1, [r0, #0x00]
/*DMC0 Drive Strength (Setting 2X)*/
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_0DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_1DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_2DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_3DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_4DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_5DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_6DRV_SR_OFFSET]
ldr r1, =0x0000AAAA
str r1, [r0, #MP1_7DRV_SR_OFFSET]
ldr r1, =0x00002AAA
str r1, [r0, #MP1_8DRV_SR_OFFSET]
/* DMC0 initialization at single Type*/
ldr r0, =APB_DMC_0_BASE //DMC0 基地址
ldr r1, =0x00101000 //ctrl_start_point: bit[15:8],即00010000; ctrl_inc: bit[23:16],即00010000, 这两位都设置为0x10
str r1, [r0, #DMC_PHYCONTROL0] //DMC_PHYCONTROL0偏移地址
ldr r1, =0x00000086 //PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case
str r1, [r0, #DMC_PHYCONTROL1] //DMC_PHYCONTROL1偏移地址
ldr r1, =0x00101002 //置位PhyControl0 DLL on bit:[1]
str r1, [r0, #DMC_PHYCONTROL0]
ldr r1, =0x00101003 //置位PhyControl0.ctrl_start以启动DLL,ctrl_start:bit[0]
str r1, [r0, #DMC_PHYCONTROL0]
find_lock_val:
ldr r1, [r0, #DMC_PHYSTATUS] //Load Phystatus register value
and r2, r1, #0x7
cmp r2, #0x7 //Loop until DLL is locked
bne find_lock_val
and r1, #0x3fc0
mov r2, r1, LSL #18
orr r2, r2, #0x100000
orr r2 ,r2, #0x1000
orr r1, r2, #0x3 //Force Value locking
str r1, [r0, #DMC_PHYCONTROL0]
#if 0 /* Memory margin test 10.01.05 */
orr r1, r2, #0x1 //DLL off
str r1, [r0, #DMC_PHYCONTROL0]
#endif
/* setting DDR2 */
ldr r1, =0x0FFF2010 //ConControl auto refresh off
str r1, [r0, #DMC_CONCONTROL]
ldr r1, =DMC0_MEMCONTROL//MemControl BL=4, 1 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
str r1, [r0, #DMC_MEMCONTROL]
ldr r1, =DMC0_MEMCONFIG_0 //MemConfig0 256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
str r1, [r0, #DMC_MEMCONFIG0]
ldr r1, =DMC0_MEMCONFIG_1 //MemConfig1
str r1, [r0, #DMC_MEMCONFIG1]
ldr r1, =0xFF000000 //PrechConfig
str r1, [r0, #DMC_PRECHCONFIG]
ldr r1, =DMC0_TIMINGA_REF //TimingAref 7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
str r1, [r0, #DMC_TIMINGAREF]
ldr r1, =DMC0_TIMING_ROW //TimingRow for @200MHz
str r1, [r0, #DMC_TIMINGROW]
ldr r1, =DMC0_TIMING_DATA //TimingData CL=3
str r1, [r0, #DMC_TIMINGDATA]
ldr r1, =DMC0_TIMING_PWR //TimingPower
str r1, [r0, #DMC_TIMINGPOWER]
ldr r1, =0x07000000 //DirectCmd chip0 Deselect
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01000000 //DirectCmd chip0 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00020000 //DirectCmd chip0 EMRS2
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00030000 //DirectCmd chip0 EMRS3
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010400 //DirectCmd chip0 EMRS1 (MEM DLL on, DQS# disable)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00000542 //DirectCmd chip0 MRS (MEM DLL reset) CL=4, BL=4
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01000000 //DirectCmd chip0 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05000000 //DirectCmd chip0 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05000000 //DirectCmd chip0 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00000442 //DirectCmd chip0 MRS (MEM DLL unreset)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010780 //DirectCmd chip0 EMRS1 (OCD default)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00010400 //DirectCmd chip0 EMRS1 (OCD exit)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x07100000 //DirectCmd chip1 Deselect
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01100000 //DirectCmd chip1 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00120000 //DirectCmd chip1 EMRS2
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00130000 //DirectCmd chip1 EMRS3
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110400 //DirectCmd chip1 EMRS1 (MEM DLL on, DQS# disable)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00100542 //DirectCmd chip1 MRS (MEM DLL reset) CL=4, BL=4
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x01100000 //DirectCmd chip1 PALL
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05100000 //DirectCmd chip1 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x05100000 //DirectCmd chip1 REFA
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00100442 //DirectCmd chip1 MRS (MEM DLL unreset)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110780 //DirectCmd chip1 EMRS1 (OCD default)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x00110400 //DirectCmd chip1 EMRS1 (OCD exit)
str r1, [r0, #DMC_DIRECTCMD]
ldr r1, =0x0FF02030 //ConControl auto refresh on
str r1, [r0, #DMC_CONCONTROL]
ldr r1, =0xFFFF00FF //PwrdnConfig
str r1, [r0, #DMC_PWRDNCONFIG]
ldr r1, =0x00202400 //MemControl BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
str r1, [r0, #DMC_MEMCONTROL]
// 函数返回
mov pc, lr
//以上为DRAM0初始化代码
内容参考来源:朱有鹏老师课堂笔记!