上一此实验我们讲解了如何对代码进行重定位,但是将代码重定位到只有256K的IRAM中作用不大。正确的做法是将代码重定位到容量更大的主存中,即DRAM中。
Exynos4412中有两个独立的DRAM控制器,分别叫DMC0和DMC1。DMC0和DMC1分别支持最大1.5GB的DRAM,它们都支持DDR2/DDR3和LPDDR2等,512 Mb, 1 Gb, 2 Gb, 4 Gb and 8 Gbit的内存设备,支持16/32bit的位宽。DRAM0 对应的地址是0x4000_0000~0xAFFF_FFF共1.5GB,DRAM1 对应的地址是0xA000_000~0x0000_0000共1.5GB。
Tiny4412的1GB的DRAM是由4片大小为128MX16的DDR3芯片组合而成,下面看一下Tiny4412的原理图:
从上两图可以看出,这四片DDR 芯片被分成了两两一组,组成32位数据,四片都是挂接到DMC0处。
如何才能使用DRAM?对应Tiny4412而言,由于用到了DMC0,所有我们需要初始化DMC0和DDR3 DRAM芯片。
声明一下:
从这一节开始我们的程序结构发生了一些变化,前几个实验我们只生成一个文件,从这个实验开始我们生成两个文件,BL2.bin和main.bin,其中BL2.bin文件的链接地址是0x02023400;(使用的是位置无关码,程序可以在任意可用的内存中运行),main.bin 文件的链接地址是0x43E00000(使用的并不是位置无关码,所有程序必须位于该地址处才能正常运行)。需要在SD卡上烧写三部分程序,分别是:
1.BL1(由三星提供):实现一些初始化
2.BL2(我们自己编写源码,用mkbl2工具生成):板级初始化,并完成代码重定位到DDR SDRAM,完成跳转
3.主代码:实现我们想要的功能
三部分代码在SD卡的位置如下:
从图中可以看出,BL1.bin烧写到SD卡扇区1,BL2.bin烧写到sd卡的扇区17,main.bin烧写到sd卡的扇区49处。
整个程序的运行过程大致如下:系统上电后,首先将sd卡扇区1处的bl1拷贝到IRAM的0x02020000地址处,然后运行该部分代码,该部分代码首先又会加载BL2.bin,BL2.bin会进行时钟和DRAM初始化,然后把位于sd卡中扇区49处的main.bin拷贝到DRAM的0x43E00000地址处,最后跳转到该地址处继续运行。
一、程序说明
DDR的初始化顺序在前一篇文章Tiny4412裸机程序之DDR3初始化流程我们已经经过,下面就根据前面提及的步骤一一来进行设置。
注:看到这么多设置步骤,实在太繁琐了,我们参考u-boot for Tiny4412中的代码,搞明白它设置了哪些东西:
/* * (C) Copyright 2011 Samsung Electronics Co. Ltd * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include <config.h> #include <asm/arch/cpu.h> #define MCLK_400 .globl mem_ctrl_asm_init mem_ctrl_asm_init: /* Async bridge configuration at CPU_core: * 1: half_sync * 0: full_sync */ ldr r0, =0x10010350 mov r1, #1 str r1, [r0] /*这几行代码不知道什么意思,以及这样做的原因*/ /*****************************************************************/ /*DREX0***********************************************************/ /*****************************************************************/ ldr r0, =APB_DMC_0_BASE ldr r1, =0xe0000086 str r1, [r0, #DMC_PHYCONTROL1] /*2. If on die termination is required, enable PhyControl1.term_write_en, PhyControl1.term_read_en.*/ ldr r1, =0xE3854C03 str r1, [r0, #DMC_PHYZQCONTROL] /*3. If ZQ calibration is required, disable PhyZQControl. ctrl_zq_mode_noterm and enable PhyZQCon-trol. ctrl_zq_start so that the PHY automatically calibrates the I/Os to match the driving and termination impedance by referencing resistor value of an external resistor and updates the matched value during auto re-fresh cycles.*/ mov r2, #0x100000 1: subs r2, r2, #1 bne 1b ldr r1, =0x7110100A str r1, [r0, #DMC_PHYCONTROL0] /*4. 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 activate the PHY DLL.*/ ldr r1, =0xe0000086 str r1, [r0, #DMC_PHYCONTROL1] /*5. DQS Cleaning: set the PhyControl1.ctrl_shiftc and PhyControl1. ctrl_offsetc bit-fields to the proper value according to clock frequency, board delay and memory tDQSCK parameter.*/ ldr r1, =0x7110100B str r1, [r0, #DMC_PHYCONTROL0] /*6. Set the PhyControl0.ctrl_start bit-field to "1".*/ ldr r1, =0x00000000 str r1, [r0, #DMC_PHYCONTROL2] /*DQS offset*/ /*实验了一下可以省略,默认值就是全零*/ ldr r1, =0x0FFF301A str r1, [r0, #DMC_CONCONTROL] /*7. Set the ConControl. At this moment, an auto refresh counter should be off.*/ ldr r1, =0x00312640 str r1, [r0, #DMC_MEMCONTROL] /*8. Set the MemControl. At this moment, all power down modes and periodic ZQ(pzq_en) should be off.*/ ldr r1, =0x40e01323 str r1, [r0, #DMC_MEMCONFIG0] ldr r1, =0x60e01323 str r1, [r0, #DMC_MEMCONFIG1] /*9. Set the MemConfig0 register. If there are two external memory chips, also set the MemConfig1 register.*/ ldr r1, =(0x80000000 | CONFIG_IV_SIZE) str r1, [r0, #DMC_IVCONTROL] /*Memory Channel Interleaving*/ /*实验了一下可以省略,用默认值就可以*/ ldr r1, =0xff000000 str r1, [r0, #DMC_PRECHCONFIG] /*10. Set the PrechConfig and PwrdnConfig registers.*/ ldr r1, =0x000000BB str r1, [r0, #DMC_TIMINGAREF] @TimingAref ldr r1, =0x4046654f str r1, [r0, #DMC_TIMINGROW] @TimingRow ldr r1, =0x46400506 str r1, [r0, #DMC_TIMINGDATA] @TimingData ldr r1, =0x52000a3c str r1, [r0, #DMC_TIMINGPOWER] @TimingPower /*11. Set the TimingAref, TimingRow, TimingData and TimingPower registers according to memory AC parame-ters.*/ /* chip 0 */ ldr r1, =0x07000000 str r1, [r0, #DMC_DIRECTCMD] /*19. Issue a NOP command using the DirectCmd register to assert and to hold CKE to a logic high level.*/ mov r2, #0x100000 2: subs r2, r2, #1 bne 2b /*20. Wait for tXPR(max(5nCK,tRFC(min)+10ns)) or set tXP to tXPR value before step 17. If the system set tXP to tXPR, then the system must set tXP to proper value before normal memory operation.*/ ldr r1, =0x00020000 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00030000 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00010002 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00000328 str r1, [r0, #DMC_DIRECTCMD] /*没搞明白这里发的什么指令*/ mov r2, #0x100000 3: subs r2, r2, #1 bne 3b ldr r1, =0x0a000000 str r1, [r0, #DMC_DIRECTCMD] /*26. Issues a ZQINIT commands using the DirectCmd register.*/ mov r2, #0x100000 4: subs r2, r2, #1 bne 4b /*27. If there are two external memory chips, perform steps 19 ~ 26 procedures for chip1 memory device.*/ #if 1 /* chip 1 */ ldr r1, =0x07100000 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 5: subs r2, r2, #1 bne 5b ldr r1, =0x00120000 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00130000 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00110002 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00100328 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 6: subs r2, r2, #1 bne 6b ldr r1, =0x0a100000 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 7: subs r2, r2, #1 bne 7b #endif ldr r1, =0xe000008e str r1, [r0, #DMC_PHYCONTROL1] ldr r1, =0xe0000086 str r1, [r0, #DMC_PHYCONTROL1] mov r2, #0x100000 8: subs r2, r2, #1 bne 8b /*****************************************************************/ /*DREX1***********************************************************/ /*****************************************************************/ ldr r0, =APB_DMC_1_BASE ldr r1, =0xe0000086 str r1, [r0, #DMC_PHYCONTROL1] ldr r1, =0xE3854C03 str r1, [r0, #DMC_PHYZQCONTROL] mov r2, #0x100000 1: subs r2, r2, #1 bne 1b ldr r1, =0xe000008e str r1, [r0, #DMC_PHYCONTROL1] ldr r1, =0xe0000086 str r1, [r0, #DMC_PHYCONTROL1] ldr r1, =0x71101008 str r1, [r0, #DMC_PHYCONTROL0] ldr r1, =0x7110100A str r1, [r0, #DMC_PHYCONTROL0] ldr r1, =0xe0000086 str r1, [r0, #DMC_PHYCONTROL1] ldr r1, =0x7110100B str r1, [r0, #DMC_PHYCONTROL0] ldr r1, =0x00000000 str r1, [r0, #DMC_PHYCONTROL2] ldr r1, =0x0FFF301A str r1, [r0, #DMC_CONCONTROL] ldr r1, =0x00312640 str r1, [r0, #DMC_MEMCONTROL] ldr r1, =0x40e01323 @Interleaved? str r1, [r0, #DMC_MEMCONFIG0] ldr r1, =0x60e01323 str r1, [r0, #DMC_MEMCONFIG1] ldr r1, =(0x80000000 | CONFIG_IV_SIZE) str r1, [r0, #DMC_IVCONTROL] ldr r1, =0xff000000 str r1, [r0, #DMC_PRECHCONFIG] ldr r1, =0x000000BB str r1, [r0, #DMC_TIMINGAREF] @TimingAref ldr r1, =0x4046654f str r1, [r0, #DMC_TIMINGROW] @TimingRow ldr r1, =0x46400506 str r1, [r0, #DMC_TIMINGDATA] @TimingData ldr r1, =0x52000a3c str r1, [r0, #DMC_TIMINGPOWER] @TimingPower /* chip 0 */ ldr r1, =0x07000000 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 2: subs r2, r2, #1 bne 2b ldr r1, =0x00020000 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00030000 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00010002 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00000328 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 3: subs r2, r2, #1 bne 3b ldr r1, =0x0a000000 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 4: subs r2, r2, #1 bne 4b #if 1 /* chip 1 */ ldr r1, =0x07100000 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 5: subs r2, r2, #1 bne 5b ldr r1, =0x00120000 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00130000 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00110002 str r1, [r0, #DMC_DIRECTCMD] ldr r1, =0x00100328 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 6: subs r2, r2, #1 bne 6b ldr r1, =0x0a100000 str r1, [r0, #DMC_DIRECTCMD] mov r2, #0x100000 7: subs r2, r2, #1 bne 7b #endif ldr r1, =0xe000008e str r1, [r0, #DMC_PHYCONTROL1] ldr r1, =0xe0000086 str r1, [r0, #DMC_PHYCONTROL1] mov r2, #0x100000 8: subs r2, r2, #1 bne 8b /*****************************************************************/ /*Finalize********************************************************/ /*****************************************************************/ ldr r0, =APB_DMC_0_BASE ldr r1, =0x0FFF303A str r1, [r0, #DMC_CONCONTROL] /*28. Set the ConControl to turn on an auto refresh counter.*/ ldr r0, =APB_DMC_1_BASE ldr r1, =0x0FFF303A str r1, [r0, #DMC_CONCONTROL] /*28. Set the ConControl to turn on an auto refresh counter.*/ mov pc, lr
二、编译、烧写、运行
1.编译
通过FTP或者其他工具将文件上传到服务器上去,输入make命令进行编译将得到make_bl2.bin和main.bin文件。
2.烧写
将SD卡插入电脑,并让VmWare里的Ubuntu识别出来,然后执行如下命令:
sudo ./sd_fusing.sh /dev/sdb ../9_reload_ddr/BL2/make_bl2.bin ../9_reload_ddr/MAIN/main.bin
二、运行现象
将SD卡插入Tiny4412开发板,连接串口工具,上电,你会看到和上一节的运行效果一样(因为我们没有修改LED的显示效果,只是修改了程序的运行地址,这个对外是看不出区别的)。
串口可以看到如下显示信息;
从图的信息室打印的地址0x43E00000处的内容(main.bin文件的链接地址)
我们将上述打印的信息和main.bin文件进行对比,发现完全一样,说明代码已经拷贝到了正确的链接地址。
完整的程序下载地址(解压密码:WWW.techbulo.Com):