S5PV210的内存分配研究分析

S5PV210内存一般会使用SDRAM和DDR2 (DDR SDRAM),SDRAM的uboot启动网络已经有很多资料的,对于DDR2还有有很多疑惑,如果有错误的地方,请大家一定指出,醍醐灌顶,不胜感激。

1、S5PV210的memory map(物理地址)

如下图:左图是整个芯片的内存空间(物理地址),右图是iROM部分的内存空间(BL0的地址貌似不是物理地址==)

 
寻址空间是4GB=232
前512是Boot area
接下是512M的DRAM0通道和512M的DRAM1通道,是存储器的地址,用于DDR2 RAM寻址的,共1GB
后面是6个128M的SROM (Bank0~Bank5),用于总线型设备寻址的
接着256M的Nand和OneNand控制器寄存器和256M的MP3_SRAM输出缓存
包含64KB IROM (BL0) 和 96KB 片内SRAM (BL1 BL2)做为internal memory,一般是启动时用到的
后面的128M DMZ ROM还没搞明白是什么
最后面512M就是SFR了(特殊功能寄存器)
S5PV210的内存分配研究分析
 

2、U-Boot内存分配图

S5PV210的内存分配研究分析

2.u-boot映像的地址0并非指物理地址0,由不同的启动方式映射到不同的地址。例如v210是映射到0xD0000000处的irom。

3.TEXT_BASE等指向SDRAM的地址均为虚拟地址。

4.TEXT_BASE为顶层Makefile中定义的,例如三星官方BSP中定义的是0xC3E00000,它是程序实际的链接首地址。

5.SDRAM_BASE被MMU映射在0xC0000000。

6._end和__bss_start为链接脚本文件中最后定义的bss段,在链接时确定,并与u-boot映像编译在一起。

7.在bl1段运行时,u-boot映像被复制到TEXT_BASE开始的地址处。

8. u-boot分配用户栈顶的代码为:
 ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */      将0xc3e00000加载到r0
 sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */      r0减去0x4000的malloc域
 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */      r0减去128字节的全局结构体
#if defined(CONFIG_USE_IRQ)
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)   如果用户有使用IRQ,再减去2*4*1024的中断栈空间
#endif
 sub sp, r0, #12  /* leave 3 words for abort-stack    */      为取址终止异常预留3个字空间后设置好用户sp

 
 3、DDR2 内存分配映射
S5PV210有两个独立的内存控制器:DMC0和DMC1,每个控制器又有两个片选:CS0和CS1。它可以支持16bits和32bits的内存。两个控制器对应的地址空间是
DMC0 0x2000_0000 ~ 0x3FFF_FFFF             512MB
DMC1 0x4000_0000 ~ 0x7FFF_FFFF             512MB
内存控制器的配置寄存器也分为两块,
DMC0 0xF000_xxxx
DMC1 0xF140_xxxx
这一块主要需要设置的寄存器设置是:MEMCONTROL、MEMCONFIG0、MEMCONFIG1
开发板用的是4片1Gbit x16位的内存,两片两片按数据线的高16bits和低16bits组成两组32bits的256M,分别挂载了DMC0的CS0和DMC1的CS1。
S5PV210的内存分配研究分析
 
MEMCONTROL里面有个num_chip,使用了几个CS信号。两个DDR控制器只用了一个CS0,对于210来说就是一片,所以设置为 0x0 = 1 chip
然后 MEMCONTROL的mem_width,设为 0x2 = 32-bit 。
MEMCONFIG0和MEMCONFIG1是一样的,一个设置相应控制器的CS0,一个设置CS1。
 
这两个寄存器合起来,可以决定DMC0/DMC1 CS0和CS1下挂着的内存对应哪一段内存地址。我理解的是当你要放问地址0x21xx_xxxx的地址的时候,DMC0会将访问地址的高8bits 0x21和chip_mask求与,要是等于chip_base,他就会使用相应的控制器的相应的片选去读数据。就像假如
DMC0_MEMCONFIG0   chip_base =  0x20      chip_mask=0xf8
DMC0_MEMCONFIG1   chip_base =  0x28      chip_mask=0xf8
当你要访问的地址是 0x22xx_xxxx的时候,  (0x22 & 0xf8) ==  0x20,所以使用CS0
那要是  0x2exx_xxxx呢?     (0x2e & 0xf8) == 0x28 ,当然使用CS1了。
也就是说CS0对应的是 0x2000_0000 ~ 0x27ff_ffff,128M
CS1对应的是 0x2800_0000 ~ 0x28ff_ffff,128M
同样的每个CS下都是256M呢?
DMC0_MEMCONFIG0   chip_base =  0x20      chip_mask=0xf0          // 0x2000_0000 ~ 0x2fff_ffff     256M
DMC0_MEMCONFIG1   chip_base =  0x30      chip_mask=0xf0          // 0x3000_0000 ~ 0x3fff_ffff     256M
DMC0和DMC1配置一样,但是DMC0只能配置为0x2000_0000~0x3fff_ffff的空间,DMC1只能配置为0x4000_0000~0x5fff_ffff的空间。这是DMC的地址空间决定的,我就是在这郁闷了两天。
我的板子上面DMC0和DMC1上都只使用了CS0,然后我的设置就是
DMC0_MEMCONFIG0   chip_base =  0x20      chip_mask=0xf0          // 0x2000_0000 ~ 0x2fff_ffff     256M
DMC1_MEMCONFIG0   chip_base =  0x40      chip_mask=0xf0          // 0x4000_0000 ~ 0x4fff_ffff     256M  注意,DMC1只能从0x4000_0000开始。
问题又来了,这样内存地址空间不就不连续了嘛?
内存的地址空间不从0x2000_0000开始,从0x3000_0000开始,不就正好接上DMC1的了。只是CONFIG_SYS_SDRAM_BASE和CONFIG_SYS_TEXT_BASE相应的改一下:
DMC0_MEMCONFIG0   chip_base =  0x30      chip_mask=0xf0          // 0x3000_0000 ~ 0x3fff_ffff     256M
DMC1_MEMCONFIG0   chip_base =  0x40      chip_mask=0xf0          // 0x4000_0000 ~ 0x4fff_ffff     256M
 
难点:关于地址映射

如果设置chip_base为0x20:

(1)我们挂载的内存为128M,那么这个chip_mask应该设置为0xF8

(2)我们挂载256M内存时,chip_mask应该设置为0xF0

(3)我们挂载512M时,chip_mask应该设置为0xE0

(4)我们挂载1GB内存时,chip_mask就应该设置为0xC0。

以DMC0为例,当DMC0接收到来自AXI的0x2000,0000~0x3fff,ffff内的地址时,会作如下处理:

(1)将AXI地址的高8位与chip_mask相与得到结果,记为X。

(2)将X分别与MEMCONFIG0和MEMCONFIG1的chip_base相比较,如果相等,则打开相应的片选。

假如挂载的内存为128M,且CS0和CS1上分别挂了一片,那么128M=128*1024*1024=0x8000000,则128M内存的偏移范围应该是0x0000,0000~0x07ff,ffff,高位剩余5位,那么,我们把MEMCONFIG0的chip_base设置为0x20,chip_mask设置为0xF8,为了保持内存连续,则需要将MEMCONFIG1的chip_base设置为0x28,chip_mask设置为0xF8,当AXI发来的地址为0x23xx,xxxx时,0x23&0xF8得到0x20,所以,会打开片选CS0,当AXI发来的地址为0x28xx,xxxx时,0x28&0xF8得到0x28,所以,会打开片选CS1,依此类推。

特别的,当载在的内存芯片为8bank(8bank内存芯片一般为14/15行地址,10列地址,即容量一般为512M或者1G)时,由于CS1为bank2引脚,为了保持CS0时钟处于片选状态,对于512M内存来讲需要将chip_mask设置为0xE0,这是因为512M=512*1024*1024=0x2000,0000,也就是说,512M内存的偏移应该为0x0000,0000~0x1fff,ffff,所以高位剩余3位,即0xE0,当然了,如果内存为1G=1024*1024*1024=0x4000,0000,即偏移为0x0000,0000~0x3fff,ffff,高位剩余2为,故设置chip_mask为0xC0。这样,就会计算偏移这两个值了。

4、 配置流程

内存芯片的配置比较复杂,好在芯片手册上给出了常用内存类型的初始化序列,TQ210的内存是DDR2的,可以按照如下顺序进行初始化:

1. To provide stable power for controller and memory device, the controller must assert and hold CKE to a logic low level. Then apply stable clock. Note: XDDR2SEL should be High level to hold CKE to low.
2. Set the PhyControl0.ctrl_start_point and PhyControl0.ctrl_inc bit-fields to correct value according to clock frequency. Set the PhyControl0.ctrl_dll_on bit-field to ‘1’ to turn on the PHY DLL.
3. DQS Cleaning: Set the PhyControl1.ctrl_shiftc and PhyControl1.ctrl_offsetc bit-fields to correct value according to clock frequency and memory tAC parameters.
4. Set the PhyControl0.ctrl_start bit-field to ‘1’.
5. Set the ConControl. At this moment, an auto refresh counter should be off.
6. Set the MemControl. At this moment, all power down modes should be off.
7. Set the MemConfig0 register. If there are two external memo ry chips, set the MemConfig1 register.
8. Set the PrechConfig and PwrdnConfig registers.
9. Set the TimingAref, TimingRow, TimingData and TimingPower registers according to memory AC parameters.
10. If QoS scheme is required, set the QosControl0~15 and QosConfig0~15 registers.
11. Wait for the PhyStatus0.ctrl_locked bit-fields to change to ‘1’. Check whether PHY DLL is locked.
12. PHY DLL compensates the changes of delay amount caused by Process, Voltage and Temperature (PVT) variation during memory operation. Therefore, PHY DLL should not be off for reliable operation. It can be off except runs at low frequency. If off mode is used, set the PhyControl0.ctrl_force bit-field to correct value according to the PhyStatus0.ctrl_lock_value[9:2] bit-field to fix delay amount. Clear the PhyControl0.ctrl_dll_on bit-field to turn off PHY DLL.
13. Confirm whether stable clock is issued minimum 200us after power on
14. Issue a NOP command using the DirectCmd register to assert and to hold CKE to a logic high level
15. Wait for minimum 400ns.
16. Issue a PALL command using the DirectCmd register.
17. Issue an EMRS2 command using the DirectCmd register to program the operating parameters.
18. Issue an EMRS3 command using the DirectCmd register to program the operating parameters.
19. Issue an EMRS command using the DirectCmd register to enable the memory DLLs.
20. Issue a MRS command using the DirectCmd register to reset the memory DLL.
21. Issue a PALL command using the DirectCmd register.
22. Issue two Auto Refresh commands using the DirectCmd register.
23. Issue a MRS command using the DirectCmd register to program the operating parameters without resetting the memory DLL.
24. Wait for minimum 200 clock cycles.
25. Issue an EMRS command using the DirectCmd register to program the operating parameters. If OCD calibration is not used, issue an EMRS command to set OCD Calibration Default. After that, issue an EMRS command to exit OCD Calibration Mode and to program the operating parameters.
26. If there are two external memory chips, perform steps 14~25 for chip1 memory device.
27. Set the ConControl to turn on an auto refresh counter. 28. If power down modes is required, set the MemControl registers.

上面就是手册上给出的DDR2型内存芯片的初始化序列,但是单纯的根据上面的步骤配置可能有些困难,这时,我们可以参考u-boot的内存初始化代码来初始化内存,最后你会发现u-boot的操作顺序跟上面是完全一致的。

5、代码的链接地址和运行地址等注意事项

配置好内存的代码烧写到Nand运行是正常的,但是用USB方式启动时不正常,目前还没有找到原因,如果有朋友解决了或者有其他问题请留言相告,在这里先说声谢谢。

现在找到原因了,原来S5PV210的USB启动过程跟Nand启动方式不一样,S5PV210以USB方式启动时会先将三星提供的一个固件程序下载到0xd0020010处运行,然后,再将用户代码下载0x23e00000处运行,也就是说,固件程序完成了内存的初始化,因为我们的代码位于0x23e00000处。而我们的代码中再次配置内存时会重置内存,下载到内存中的代码也就丢失了,所以程序执行到内存初始化函数就会挂掉。

为了证明上面的假设,我在代码中加上一段程序,该程序将内存的中代码拷贝到iram的16K以后的位置上(直接拷贝到0xd0020000处有问题,我是拷贝到了0xd0024000处,现在还不知道什么原因),然后将代码跳转到IRAM中,如果代码可以正常运行就可以证明内存初始化部分正常,实验结果是肯定的,下面总结下:

S5PV210以USB方式启动时用户代码是下载到内存中的(0x23e00000处),要使代码以USB方式启动时正常运行,应该注意以下两点:

(1)如果是位置相关的代码,连接地址应该链接到0x23e00000,如果是位置无关码,可以随便指定连接地址。
(2)用户代码需要检验自己运行时的位置,如果运行在内存中则需跳过内存初始化,根据需要决定是否需要代码重定位。

参考网址:http://blog.chinaunix.net/uid-122754-id-3144920.html

       http://www.cnblogs.com/Efronc/archive/2012/03/01/2375578.html

上一篇:用随机投掷飞镖法计算Pi值(Randomness Throwing dart Pi Python)


下一篇:python flask框架 tempates 模版的使用