2015.10uboot移植笔记 (四、低级初始化lowlevel_init续)

想不到一篇既然写不完lowlevel_init,有点失算,只要是要把时钟系统介绍好,涉及到很多知识,如果想好好了解时钟系统的话,可以直接去看数据手册,不过总感觉我拿到的数据手册应该少了一些东西,就是寄存器有点不全。反正影响也不大,接下来继续分析lowlevel_init。

1、开始移植ddr

本来官网的2015版的uboot是没有初始化ddr内存的,所以就找了一个三星移植过的uboot,里面就有ddr内存的初始化,这个文件就是cpu/s5pc11x/s5pc110/cpu_init.S,这个是三星已经移植好的了,要我自己从0开始写ddr汇编程序,功力还是不够,所以只能移植,这个文件拷贝过来放在哪里呢?三星官网是放到cpu下的,我是放到board/samsung/goni,具体放两个位置都可以,就看你怎么决定。
文件已经拷贝过来了,然后就是添加Makefile。让这个文件参与编译和链接,Makefile就在文件放置的目录下的,打开Makefile,我已经添加好了,直接上程序,按照写就可以了。

#
# (C) Copyright 2000, 2001, 2002
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2008
# Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de>
#
# SPDX-License-Identifier:	GPL-2.0+
#

obj-y	:= goni.o onenand.o
obj-y	+= lowlevel_init.o
obj-y	+= cpu_init.o
obj-y	+= led.o
obj-y	+= movi.o

并且还在这个函数返回后,添加一个点灯程序,测试一下这个程序是否有问题。

	bl mem_ctrl_asm_init	//ddr内存初始化函数
	bl led3					//自己实现的一个点灯程序,其实里面就是操作gpio的几个寄存器,上一节好像有讲
	bl test

添加好了,就可以编译链接,烧录到sd卡,启动板子,结果灯不亮,没事,不要怕,我们进入到mem_ctrl_asm_init这个函数里面,把点灯程序加入,结果发现灯还没亮,重点来了,请注意:

按照正常情况,我们函数调用了,就应该会执行啊,那为什么灯不会亮,这就涉及到s5pv210启动有关的程序了,反正下面也是要准备做重定位,可以再这里讲一讲

首先,启动顺序我们在第一篇的时候已经讲到了,210启动首先执行内部的iROM(也就是BL0),BL0会判断OMpin(这个值文档没有给出,不过可以通过代码来看)来决定从哪个设备启动,如果启动设备是mmc,就会去找对应的mmc。
其实后面还是有一些操作的,之前是没有讲,现在可以补上了
在这里插入图片描述
这个也是三星的数据手册,跟硬件有关的可以有空没空看看数据手册,图中显示
第一步,iROM初始化和启动
第二步,iROM把BL1拷贝到SRAM中执行。(这一段就是我们写的uboot的前16k)
第三步,BL1把剩下的启动程序BL2也拷贝到SRAM中运行。(不过我们uboot直接忽略了这步)
第四步,BL2初始化DDR和把OS操作系统加载到DDR中。(其实我们这个是在BL1做的)
第五步,拷贝完成,实现远跳转。(就是从SRAM中的地址跳转到DDR中的地址)

可以来一个具体的流程图,好好了解了解
在这里插入图片描述
我们现在做的就是第三步,初始化DDR,结果没初始化成功,好了,说了这么多,我们回归原来问题,为什么灯不亮,是因为我们BL1大小太小了,我们在第一篇就有写过制作镜像的脚本,脚本是会把前面的8k的程序提取出来做为BL1,mem_ctrl_asm_init 有可能已经超出我们的8K了,怎么认证呢?
我们通过打开u-boot.map,这个文件是记录每一个函数的地址,我们在这个文件中找到了mem_ctrl_asm_init 这个函数:

 .text          0x3480198c      0x8e8 board/samsung/goni/built-in.o
                0x3480198c                lowlevel_init
                0x34801e8c                mem_ctrl_asm_init

地址为0x34801e8c,这个我们uboot的起始地址为0x34800000,这个值定义在s5p_goni.h上,

/* Text Base */
#define CONFIG_SYS_TEXT_BASE		0x34800000		//uboot在DDR中的起始地址

我们可以算出,mem_ctrl_asm_init函数在BL1中7.63671875K的位置上,所以说这个函数很有可能超出范围了,所以我们只要把制作镜像脚本改为10K,再试试

#<BL1 fusing>
bl1_position=1
uboot_position=49

echo "BL1 fusing"
./mkbl1 ../u-boot.bin SD-bl1-8k.bin 10192		//改为10K
dd iflag=dsync oflag=dsync if=SD-bl1-8k.bin of=$1 seek=$bl1_position
rm SD-bl1-8k.bin

####################################

再次编译,烧录到板子上,就可以看到灯已经亮了起来,说明我们之前的分析是对的。

2、分析内存代码

简单过一篇代码就可以了,比较初始化内存是比较复杂的一件事。


/* DMC0 Drive Strength (Setting 2X) */
	//从数据手册我们可以看出
	//MP1_0~8: 71 DRAM1 ports (in/out port is not used)
	//MP2_0~8: 71 DRAM2 ports (in/out port is not used)
	//MP1 多有端口就是给DRAM1使用的,MP2就是给DRAM2使用的
	//把MP1和MP2所有的gpio的输出能力设为2x
	ldr	r0, =ELFIN_GPIO_BASE			//   0xE0200000

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_0DRV_SR_OFFSET]	//	0x3CC

	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]


	/* DMC1 Drive Strength (Setting 2X) */
	
	ldr	r0, =ELFIN_GPIO_BASE
	
	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_0DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_1DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_2DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_3DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_4DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_5DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_6DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_7DRV_SR_OFFSET]

	ldr	r1, =0x00002AAA
	str	r1, [r0, #MP2_8DRV_SR_OFFSET]

可以在数据手册的107页看到gpio口的复用功能,所以也就能看到MP1是不是真的是DRAM1要使用的引脚
在这里插入图片描述
gpio设置好了之后,就可以开始初始化DRAM了,初始化DRAM是有一定的顺序的,并且我使用的这块板是DDR2,所以要找到DDR2有关的初始化步骤。DDR2有27个步骤,接下来就分析一下下。(有一些我也不懂,哈哈哈)

/* DMC0 initialization at single Type*/
	ldr	r0, =APB_DMC_0_BASE

	ldr	r1, =0x00101000				@PhyControl0 DLL parameter setting, manual 0x00101000
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00000086				@PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0x00101002				@PhyControl0 DLL on
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00101003				@PhyControl0 DLL start
	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]

这几句代码,对应的是前4个步骤:

  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’.
    感觉是要打开DLL时钟有关的,并且在最后循环等待这个时钟稳定,详细可以看上面英文步骤
/* setting DDR2 */
	ldr	r1, =0x0FFF2010				@ConControl auto refresh off
	str	r1, [r0, #DMC_CONCONTROL]   //对应第5步

	ldr	r1, =DMC0_MEMCONTROL		//0x00212400	
	//MemControl BL=4, 1 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]   //对应第6步
	
	ldr	r1, =DMC0_MEMCONFIG_0		//0x30F01323
	//MemConfig0 256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
	str	r1, [r0, #DMC_MEMCONFIG0]	//对应第7步

	ldr	r1, =DMC0_MEMCONFIG_1		//0x40F01313
	//MemConfig1
	str	r1, [r0, #DMC_MEMCONFIG1]		//对应第7步

	ldr	r1, =0xFF000000				@PrechConfig
	str	r1, [r0, #DMC_PRECHCONFIG]		//对应第8步,想自己了解的可以自己研究
  1. Set the ConControl. At this moment, an auto refresh counter should be off.
  2. Set the MemControl. At this moment, all power down modes should be off.
  3. Set the MemConfig0 register. If there are two external memory chips, set the MemConfig1 register.
  4. Set the PrechConfig and PwrdnConfig registers.

第6步比较重要的,涉及到我们设置的几个参数,可以简单的分析一下:
我这块板子是接了4片内存芯片,每块128M,4块一共512M,每一块内存芯片的数据是16位但是我们硬件上是用并联的方式接的,两块16位的内存芯片拼接成一个32位的内存芯片,所以下面的寄存器我们选择32bit,芯片个数选择1chip,虽然物理上的是两颗芯片,但是逻辑上是一颗32位256M的内存芯片。
在这里插入图片描述第7步也很重要,可以分析一下:
chip_base:是可以指定内存地址的寄存器,我们这里设置的DMC1=0x30,就是从0x30000000开始
chip_mask:对应的是计算内存芯片大小的寄存器,这里是使用掩码的方式来计算的,256M的大小转化成16进制是0x10000000
对应的内存就是0x30000000-0x3FFFFFFF,所以这里的掩码应该是0xF0,这样反过来就对应0x0FFFFFFF,所以这个寄存器应该填0xF0
chip_map:我也不知道,按照写就可以了。
chip_col chip_row chip_bank 这三个寄存器跟内存芯片有关了
在这里插入图片描述
接下来简单介绍一下内存芯片寻址方式:
在这里插入图片描述
我现在用的芯片就是这一款芯片,128M,一共有8blank,一个blank有128M/8=16M
左边的Address Register输入有A0-A13和BA0-BA2,其中的BA0-BA2就是blank选择引脚,一共有3个引脚,23=8
接下来的A0-A13就是地址线,地址线到内存芯片中会分为Column Address Bits 和Row Address Bits,我们看图片可以得出Column Address Bits 等于10位,Row Address Bits为14位,可以计算一下
210214=224=2410241024=16M

这个刚好就得到我们一个blank的小,一共有8个,就是16M8=128M

因为我们这块板只接了一个内存芯片,所以DMC0_MEMCONFIG_1可以不设置,不过还是尽量保持默认值。

	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]

这是第九步,Set the TimingAref, TimingRow, TimingData and TimingPower registers according to memory AC
parameters.这些都是设置时序有关的寄存器,感兴趣的可以研究研究。

  1. If QoS scheme is required, set the QosControl0-15 and QosConfig0-15 registers.我也不清楚这一步在那
  2. Wait for the PhyStatus0.ctrl_locked bit-fields to change to ‘1’. Check whether PHY DLL is locked.
  3. 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.
  4. Confirm whether stable clock is issued minimum 200us after power on
    第11、12步,在上面已经做了,就是设置了DLL的时候,已经在循环等待了,
	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]

接下来这一批都是控制命令,就是初始化的时候需要按照顺序输入那些命令
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.

	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]

最后的就是对应最后两步了
26.If there are two external memory chips, perform steps 14~25 for chip1 memory device。这个是说chip1的
27.Set the ConControl to turn on an auto refresh counter
28. If power down modes is required, set the MemControl registers

自此内存芯片0就初始化完成,后面的是初始化内存芯片1,基本上跟内存芯片0差不多的步骤。

3、重定位

内存初始化确实麻烦,接下来我们继续往下,

	cmp	r7, r8
	/* Clear wakeup status register */
	//清除唤醒标志
	ldreq	r0, =S5PC100_WAKEUP_STAT
	ldrne	r0, =S5PC110_WAKEUP_STAT
	ldr	r1, [r0]
	str	r1, [r0]

	/* IO retension release */
	//这个数据手册没看到,只有三星的才知道吧
	ldreq	r0, =S5PC100_OTHERS			@ 0xE0108200
	ldrne	r0, =S5PC110_OTHERS			@ 0xE010E000
	ldr	r1, [r0]
	ldreq	r2, =(1 << 31)				@ IO_RET_REL
	ldrne	r2, =((1 << 31) | (1 << 30) | (1 << 29) | (1 << 28))
	orr	r1, r1, r2
	str	r1, [r0]

接下到重点了,重定位
这里只要是实现了一个拷贝的函数(其实也不是自己实现的,是三星内部已经有了,具体的可以往下看)

void movi_bl2_copy(void)     //就是这个函数实现了重定位
{
	ulong ch;
#if 1
	ch = *(volatile u32 *)(0xD0037488);			//这一个地址里面存的是一个有意义的数据,我们看一下
	copy_sd_mmc_to_mem copy_bl2 =
	    (copy_sd_mmc_to_mem) (*(u32 *) (0xD0037F98));
#else
	ch = *(volatile u32 *)(0xD003A508);
	copy_sd_mmc_to_mem copy_bl2 =
	    (copy_sd_mmc_to_mem) (*(u32 *) (0xD003E008));
#endif
	u32 ret;

	if (ch == 0xEB000000) {
		ret = copy_bl2(0, SD_START_BLOCK, SD_BLOCK_CNT,
			CONFIG_SYS_TEXT_BASE, 0);
	}
	else if (ch == 0xEB200000) {
		ret = copy_bl2(2, SD_START_BLOCK, SD_BLOCK_CNT,
			CONFIG_SYS_TEXT_BASE, 0);
	}

	if (ret == 0)
		while (1)
			;
	else
		return;
}

在手册上,我们可以查到0xD0037488,这个地址的数据的作用,
Current boot channel:当前启动的通道。我们知道三星启动介质有几种(mmc,Nand,eSSD,eMMC等),不同的启动介质对应不同的通道,这个在手册上没写,只有三星的才知道,不过我们知道0xEB000000和0xEB200000是对应的mmc的通道0和通道2,所以我们要判断一下这个通道是不是mmc或sd卡。

在这里插入图片描述
**0xD0037F98 *这个地址其实也有特定的意义:
在这里插入图片描述
这几个是三星内部集成的设备拷贝函数,所以我们看到0xD0037F98 这个地址对应的是CopySDMMCtoMem,其实这个地址就是存放这个函数的地址,也就是一个函数指针,所以我们需要做一次转化。
这个指针的类型数据手册里面也给出了:
在这里插入图片描述
#define CopySDMMCtoMem(z,a,b,c,e)(((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned
int *)0xD0037F98)))(z,a,b,c,e))
参数1:s32 ch:是通道号
参数2:u32 StartBlkAddress :开始块的地址(下面会介绍)
参数3:u16 blockSize:块的个数
参数4:u32
memoryPtr:需要拷贝到的内存地址
参数5:bool with_init:我也不清楚
返回值:bool(u8) - Success or failure;

既然三星都内部集成了拷贝函数,我们就不用自己写了,所以就直接调用即可。
ret = copy_bl2(2, SD_START_BLOCK, SD_BLOCK_CNT, CFG_PHY_UBOOT_BASE, 0);

1.因为我们是外插sd卡,所以使用的是通道2,第一个参数写2
2.开始的块,我们利用脚本把程序写入sd卡的时候,是指定了一个地址的,
我们来看一下烧录脚本

#<BL1 fusing>
bl1_position=1
uboot_position=49

echo "BL1 fusing"
./mkbl1 ../u-boot.bin SD-bl1-8k.bin 10192  //最后一个参数指定BL1大小
dd iflag=dsync oflag=dsync if=SD-bl1-8k.bin of=$1 seek=$bl1_position
rm SD-bl1-8k.bin

####################################
#<u-boot fusing>
echo "u-boot fusing"
dd iflag=dsync oflag=dsync if=../u-boot.bin of=$1 seek=$uboot_position

####################################
#<Message Display>
echo "U-boot image is fused successfully."
echo "Eject SD card and insert it again."

我们看到烧录脚本里面有两个变量:
bl1_position=1 这个就是bl1的起始地址
uboot_position=49 这个就是整个uboot的其实地址
这个烧录脚本的原理是这样的,它首先会获取uboot前面的10k(这个是参数的我们指定的,但是最大值为16K)作为BL1,按照bl1_position的其实地址烧录进去,然后再把整个uboot的烧录到uboot_position这个地址。
为什么BL1的地址不是从0开始,这是有原因的。
在这里插入图片描述
因为这个是手册上写明了,要保留第一个block,所以从Block1开始。这里也注明了1Block=512B

3.块的个数,我们假设uboot的大小为512K(当然不会有这么大的,应该有个200多k),然后每一个block大小为512B,所以块的个数:(512 * 1024 / 512)
4.需要拷贝的内存地址,这个就是整个uboot的链接地址,我们知道整个uboot的链接地址是由s5p_goni.h里面定义的,
#define CONFIG_SYS_TEXT_BASE 0x34800000
所以我们指定链接地址为CONFIG_SYS_TEXT_BASE
5.最后一个参数就默认写就可以了

这样我们就把这个uboot重定位到ddr中,其实地址就是CONFIG_SYS_TEXT_BASE

最后为了表示我们ddr内存初始化成功和重定位成功,我们在这里加一个打印,打印一个K,

	/* Print 'K' */
	ldr	r0, =ELFIN_UART_CONSOLE_BASE
	ldr	r1, =0x4b4b4b4b
	str	r1, [r0, #UTXH_OFFSET]

这样就跟我们之前打印的一个O,拼接起来就是一个“OK”

4、长跳转

其实前面说了这么多,就是为了这里的长跳转,在刚开始我们已经看到了我们的程序已经超过了10K,也就是要超过了BL1,所以我们做了ddr内存初始化和代码重定位,就是为了在这里进行长跳转,把运行程序的内存从iRAM中跳转到ddr中,这个长跳转只有一条代码,我把它放到了start.S中。

	ldr	pc, =_main

这样就长跳转到了_main这个函数中了,有关_main这个函数的介绍,放到下一节。

由于水平有限,讲的哪里有错的地方,随时联系我,好让我及时改正。

发布了32 篇原创文章 · 获赞 26 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/C1033177205/article/details/91346290