S5PV210 SDRAM内存初始化

前言:内存为九鼎 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初始化代码

内容参考来源:朱有鹏老师课堂笔记!

你可能感兴趣的:(ARM)