大家都知道,arm在Nand flash启动模式下启动时系统会将Nand flash中的前4KB代码拷贝到SRAM(也就是Steppingstone中),由SRAM配置中断向量表和完成Nand flash访问的必要初始化,然后将Nand flash中的全部程序代码拷贝到SDRAM中,最后由SRAM跳转到SDRAM,然后程序就正常执行了,这一过程看上去很简单,但是真正理解这一过程还是不简单的,尽管这样,还是想告诉大家仔细理解还是比较容易理解这个过程的。如果您是ADS用户,你省去了很多麻烦,但我不确定你省去的这些麻烦是否值得,这里介绍的是一种麻烦的方式,linux下的led程序。
代码Head.s
- .extern main
- .text
- .global _start
- _start:
- b reset
- reset:
- ldr sp,=4096
- bl disable_watch_dog
- bl clock_init
- bl memsetup
- bl copy_steppingstone_to_sdram
- ldr pc,=on_sdram
- on_sdram:
- msr cpsr_c,#0xdf
- ldr sp,=0x34000000
- ldr lr,=halt_loop
- ldr pc,=Main
- halt_loop:
- b halt_loop
我认为,最需要理解的就是这段代码了。先简单的解释下这段代码。
(1)由于arm执行reset之后pc会被清零,也就是reset中断的中断入口地址,因此,第一条指令就是b reset,跳转到reset中断处理函数。
(2)由于这里硬件配置都是C语言来完成的,而且我们的初始代码比较小,完全不会超出4KB,因此可以在SRAM使用堆栈,故将SP设置为4096,以提供C运行环境
(3)接下来的3个bl分别完成了关闭看门够定时器,配置时钟信号和存储器配置的工作,第四个bl是将SRAM的4KB空间内的代码拷贝到了SDRAM中。
(4)接下来的ldr句将pc赋值为on_sdram的地址,实现了从SRAM到SDRAM的跳转(下面会讲为什么)
(5)on_sdram中切换到了了系统模式然后分配了系统模式堆栈,将链接寄存器设置为halt_loop然后就跳转到了SDRAM中的Main
上面的解释只是大体上说明了代码的意思,但是初学者总会有个疑问就是为什么ldr pc,=on_sdram就实现了从SRAM到SDRAM的跳转呢?我被这个问题困扰了很长时间,到今天才想明白了这个问题,问题的关键就是相对跳转和绝对跳转的问题。为了说明这个问题我先解释一下bl指令跟ldr指令在执行过程中的区别。
B指令是相对跳转指令,B 指令是最简单的跳转指令。一旦遇到一个 B 指令,ARM 处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC 值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(前后32MB 的地址空间),同样的,BL、BX都是相对跳转。
LDR伪指令是将第二操作直接赋值给第一操作数,当执行ldr pc,=Main时是将Main的绝对地址赋值给了PC。
好了,知道这两个指令的区别之后我们来看代码是如何实现的从SRAM到SDRAM的跳转,首先需要指出,2440的开发板有SRAM和SDRAM,SRAM是从地址0x00000000开始的4KB内存空间,SDRAM是从0x30000000开始的64M空间。
无论用ADS编译还是用arm-linux-gcc编译都会将代码链接到0x30000000以后(也就是SDRAM中),ADS用户可以通过查看ADS的工程配置,其中有项配置是RO起始地址是0x30000000,linux用户在链接时需要用-T指定代码的其实地址为0x30000000。
根据编译原理,在链接阶段程序中函数的地址就已经确定了,也就是说函数的实际地址都在0x30000000之后,而程序的入口函数也就是这里的_start的地址就是0x300000000,其他函数都会大于这个数。
但是由于arm上电后系统会将Nand flash的前4KB代码拷贝到SRAM中,也就是_start函数开始的4KB指令将被拷贝到SRAM中执行,根据上例,在0x00000000处执行的指令就是“b reset”,由于b是相对跳转,是在当前pc值的基础上加减某个数而跳转到将要执行的代码处,因此,pc加减该数之后将到达reset函数的位置,故reset函数不能写到4KB之外的空间中,否则arm的启动将会失败,同样的,接下来的几个bl都是执行的相对跳转,所以都相对当前pc进行的跳转,由于Nand flash总共只有64M的空间,所以相对跳转是不可能会跳转到SDRAM的,因为跳转到SDRAM至少要发生0x30000000的跳转,而这个相对位移远远大于64M。
而ldr pc,=Main是将Main函数的实际地址赋值给pc,而Main的实际地址是在0x30000000之后,这样,就从SRAM跳转到了SDRAM。
由于这个过程设计到了硬件格局和编译原理,所以对一般人来讲,理解起来确实比较困难,而且受本人水平限制,很多地方只能说是只可意会不可言传,如果误导了大家请大家谅解。当然如果看到这里还不能理解arm的启动过程可以留言讨论这个问题。下面是相关的其他代码,我附在这里,2440addr.h没有贴出,由于我也是使用arm自带示例程序中的代码,而且代码有四千多行,多数地址是没有用到的。其他的代码如下
代码Init.s- #include "2440addr.h"
- void disable_watch_dog(void);
- void clock_init(void);
- void memsetup(void);
- void copy_steppingstone_to_sdram(void);
- void inituart(void);
- void disable_watch_dog(void)
- {
- rWTCON = 0;
- }
- void clock_init(void)
- {
- rCLKDIVN = 0x03;
- /*
- *如果HDIVN非0,CPU的总线模式应该从
- *“fast bus mode”变为“asynchronous
- *bus mode”
- */
- __asm__(
- "mrc p15, 0, r1, c1, c0, 0 "
- "orr r1, r1, #0xc0000000 "
- "mcr p15, 0, r1, c1, c0, 0 "
- );
- rMPLLCON = (92<<12)|(1<<4)|(2);
- //rMPLLCON = ((0x5c<<12)|(0x01<<4)|(0x02));
- }
- void memsetup(void)
- {
- volatile unsigned long *p = (volatile unsigned long *)0x48000000;
- /* 这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值
- * 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
- * SDRAM之前就可以在steppingstone中运行
- */
- /* 存储控制器13个寄存器的值 */
- p[0] = 0x22011110; //BWSCON
- p[1] = 0x00000700; //BANKCON0
- p[2] = 0x00000700; //BANKCON1
- p[3] = 0x00000700; //BANKCON2
- p[4] = 0x00000700; //BANKCON3
- p[5] = 0x00000700; //BANKCON4
- p[6] = 0x00000700; //BANKCON5
- p[7] = 0x00018005; //BANKCON6
- p[8] = 0x00018005; //BANKCON7
- /* REFRESH,
- * HCLK=12MHz: 0x008C07A3,
- * HCLK=100MHz: 0x008C04F4
- */
- p[9] = 0x008C04F4;
- p[10] = 0x000000B1; //BANKSIZE
- p[11] = 0x00000030; //MRSRB6
- p[12] = 0x00000030; //MRSRB7
- }
- void copy_steppingstone_to_sdram(void)
- {
- unsigned int *pdwSrc = (unsigned int *)0;
- unsigned int *pdwDest = (unsigned int *)0x30000000;
- while (pdwSrc < (unsigned int *)4096)
- {
- *pdwDest = *pdwSrc;
- pdwDest++;
- pdwSrc++;
- }
- }
代码Main.c:
- #include "2440addr.h"
- void Delay(int i)
- {
- int m,n,p;
- for(m = 0; m != i; ++ m)
- {
- for(n = 0; n != 255; ++ n)
- {
- for(p = 0; p != 255; ++ p)
- ;
- }
- }
- }
- void Main()
- {
- int count;
- int leds[4] = {0x1c0, 0x1a0, 0x160, 0xe0};
- rGPBCON=0x00155555;
- rGPBUP=rGPBUP&0xFF00;
- while(1)
- {
- for(count = 0; count != 4; ++ count)
- {
- rGPBDAT=leds[count];
- if(count%2)
- rGPBDAT+=1;
- Delay(2);
- }
- }
- }
链接文件led.lds如下:
- SECTIONS
- {
- . = 0x30000000;
- .text : {*(.text)}
- .rodata ALIGN(4) : {*(.rodata)}
- .data ALIGN(4) : {*(.data)}
- .bss ALIGN(4) : {*(.bss) *(COMMON)}
- }
makefile如下:
- objects:=Head.o Init.o Main.o
- led.bin : $(objects)
- arm-linux-ld -Tled.lds -nostdlib -o led_elf $^
- arm-linux-objcopy -O binary -S led_elf $@
- arm-linux-objdump -D -m arm led_elf > led.dis
- %.o:%.c
- arm-linux-gcc -Wall -O2 -c -o $@ $<
- %.o:%.s
- arm-linux-gcc -Wall -O2 -c -o $@ $<;
- .PYTHON:clean
- clean:
- rm -f led.bin led_elf led.dis *.o
如上除了2440addr.h之外就都全了,另外需要指出的是2440addr.h中引用了Option.h,为了简化代码,可以将这句可以注释掉,在我们这段代码中完全用不到该文件相关功能。否则需要自行修改makefile文件完成Option.h相关的编译和链接工作。
好了,又浪费了大家这么长的时间,这里就不多说了,有什么问题求高手指出来。
上一篇:Linux内核的Nand驱动流程分析
下一篇:S3C6410启动模式介绍
推荐阅读最新更新时间:2024-03-16 14:29
设计资源 培训 开发板 精华推荐
- Waymo打造最大弱势道路使用者交通事故数据集 可帮助指导自动驾驶系统研发
- 车载显示,大步向前
- 新专利:未来福特汽车或将配备亮度管理系统
- 科学家研发基于AI的身份验证工具 可保护车辆免受网络攻击威胁
- Microchip推出广泛的IGBT 7 功率器件组合,专为可持续发展、电动出行和数据中心应用而设计
- 面向未来驾驶体验 博世推出新型微电子技术
- 英飞凌与马瑞利合作 利用AURIX™ TC4x MCU系列推动区域控制单元创新
- 5C超充,该怎么卷?
- 《2025年度中国汽车十大技术趋势》正式揭晓!你最看好哪个?
- Microchip推出新型VelocityDRIVE™软件平台和车规级多千兆位以太网交换芯片,支持软件定义汽车