众所周知,ok6410开发板是一块基于s3c6410芯片的开发板,板载资源丰富。s3c6410是三星电子生产的基于arm11内核的芯片。本文旨在总结一下bootloader操作步骤,用于以后复习、查找。通过分析bootloader行业老大哥uboot代码,总结出要实现OK6410开发板的启动引导,只要实现如下的操作即可: 1. 设置异常向量表; 2. 设置处理器模式为svc模式; 3. 外设基地址初始化; 4. 关闭看门狗; 5. 关闭所有中断; 6. 关闭MMU和cache; 7. 初始化系统时钟; 8. 内存初始化; 9. 复制内存; 10.栈初始化; 11.清除bss段; 12.跳入c函数(点亮led)。 下面一步一步的介绍如何实现它们。 1. 设置异常向量表:
想要设置arm的异常向量表,首先得知道其有那些异常,查看arm架构参考手册Programmers’ Model 章节的A2.6 Exceptions我们知道其共有7中异常,如下图:
设置异常向量表,按我个人的理解其实就是将异常发生时要处理的地址指向PC即可,所以通过如下代码来实现: .text.global _start_start: b reset ldr pc, _undifined_instruction @ 将pc指针指向地址_undefined_instruction处,即当该异常发生时处理器跳到标号undifined_instruction处执行 ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq _undifined_instruction: .word undifined_instruction @ .word 伪指令,用于取地址,该段表示_undefined_instruction是一个指向标号 @ undifined_instruction处的32位地址_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word resetundifined_instruction:nopsoftware_interrupt:nopprefetch_abort:nopdata_abort:nopnot_used:nopirq:nopfiq:nop开头的.text告诉编译器下面的内容是代码段,.global _start告诉编译器_start是全局的。细心的人可能会发现,明明只有7种异常,为什么会有8个异常向量表,多出来的not_used是啥?从字面理解,它是一个未使用的异常,细心阅读arm架构参考手册就会发现有下面这么一段话
地址0x00000014处向量是一个未使用的,预留的向量表,从异常处理模式图也能发现0x00000010和0x00000018之间不是连续的,如下图:
为了异常的连续性,或者说是异常发生能够正确找到异常向量表,我们需要补全这个乡里表。 分析代码可知,首先跳转到reset处执行代码,那么在reset处都干啥呢了,我们可以找到reset处看一下,代码如下: reset: bl set_svc @ 设置cpu为svc模式 bl set_peri_port @ 外设基地址初始化 bl disable_watchdog @ 关闭看门狗 bl disable_interrupt @ 关闭所有中断 bl disable_mmu @ 关闭mmu和cache bl init_clock @ 时钟初始化 bl mem_init @ 内存初始化 bl copy_to_ram @ 拷贝bl到内存 bl init_stack @ 栈初始化 bl clean_bss @ 初始化bss段 ldr pc, =main @ 进入c语言编程环境,main函数原来后面的操作都是从reset处一次执行的,下面一步一步的介绍接下来的操作。 2.设置处理器模式为svc模式:bl set_svc 通过查看arm架构参考手册“A2.2 Processor modes ”章节可以知道,arm共有7种工作模式,如下:
要想cpu工作在svc模式下,首先我们来了解一下arm的程序状态寄存器CPSR,它可以用来改变工作模式等操作,CPSR格式如下:
它的低5位就是模式选择,通过查看模式为设置,可知只要将cpsr的第五位设置成0b10011就可以了,如下:
理论上,到此就可以了,但是,我们发现cpsr的I、F位是关于中断的,这里顺便也将其配置了,关闭IRQ和FIQ如下图:
代码如下: @ set the cpu to SVC32 modeset_svc: mrs r0,cpsr @ 将程序状态寄存器取出保存在通用寄存器r0中 bic r0,r0,#0x1f @ 将从状态寄存器取出的值(r0)低5位清零后存入r0中 orr r0,r0,#0xd3 @ 将r0中的值的第0 1 4 6 7位置1后存入r0中,关闭中断与快速中断,进入svc模式 msr cpsr,r0 @ 将r0的中的值写入程序状态寄存器 mov pc, lr @ 返回调用的地方下一个地址处继续执行2.外设基地址初始化:bl set_peri_port 这一步必须要做,而且必须在这一步做,否则cpu无法正常使用外设。要想初始化s3c6410的外设基地址,首先得知道外设基地址是从0x7000000开始的,同时还得了解一下arm11的协处理器p15的内容,参考arm11内核手册,找到系统协处理器章节,找到关于Peripheral Port Memory Remap Register的协处理器命令,如下:
跳到page 3-130章节处,查看其详细内容。该寄存器格式如下:
[31:12]:表示基地址,这里我们需要将其设成0x70000000, [11:5]:不要考虑,保留默认值即可, [4:0]:外设基地址的大小,s3c6410外设需要256M大小的内存。 查看Peripheral Port Memory Remap Register bit functions ,如下图:
通过上图可知只要往p15的c15中写入0x70000013即可。代码如下: @ 初始化外设地址,必须做这一步,要不外设无法使用。set_peri_port: ldr r0, =0x70000000 @ 将基地址0x70000000存入寄存器r0中 orr r0, r0, #0x13 @ 将寄存器r0的值设为0x70000013 mcr p15,0,r0,c15,c2,4 @ 将寄存器r0的值传送到协处理器p15的c15寄存器中,参看arm11内核手册:c15, Peripheral Port Memory Remap Register mov pc, lr @ 返回调用处继续往下执行3.关闭看门狗:bl disable_watchdog 要想关闭看门狗,需要找到看门狗控制寄存器地址,通过查看s3c6410手册Watch Dog章节可以找到,如下:
看门狗控制寄存器位操作说明如下:
红框中的就是和关闭看门狗有关的位,只要将这几个位设置为0,即将看门狗控制寄存器设为0。代码如下: @ Disable Watchdog 参看s3c6410手册:Watch Dog章节#define pWTCON 0x7e004000 @ addr of watchdog timer control register (WTCON)disable_watchdog: ldr r0, =pWTCON @ 将看门狗控制寄存器地址写入寄存器r0中 mov r1, #0x0 @ 将0x0保存在寄存器r1中 str r1, [r0] @ 将0x0传送到看门狗控制寄存器中 mov pc, lr @ 返回调用处继续往下执行4.关闭所有中断:bl disable_interrupt
可能有人会问,在前面设置cpu为svc工作模式时不是已经关闭了IRQ和FIQ吗,这里还需做啥操作呢?之前的只是关闭了中断,但是并没有清除中断使能位,这里就是进行这样的操作,清除所有中断的使能位。s3c6410共有两个中断控制器VIC0、VIC1。所以需要分别找到这两个INTERRUPT ENABLE CLEAR 寄存器,并且对其进行配置,如下图:
通过上图,可知只要将其设置为0 即可,代码如下: @ Disable all interrupts (VIC0 and VIC1),参考s3c6410手册:Vectored Interrupt Controller章节disable_interrupt: mvn r1,#0x0 ldr r0,=0x71200014 @ VIC0INTENCLEAR地址 str r1,[r0] @ 将VIC0INTENCLEAR设为0 ldr r0,=0x71300014 @ VIC1INTENCLEAR地址 str r1,[r0] @ 将VIC1INTENCLEAR设为0 mov pc, lr @ 返回调用处继续往下执行5.关闭mmu和cache MMU是MemoryManagement Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。 Cache:高速缓冲存储器,是位于CPU和主存储器DRAM(DynamicRandomAccessMemory)之间,规模较小,但速度很高的存储器,通常由SRAM(StaticRandomAccessMemory静态存储器)组成。它是位于CPU与内存间的一种容量较小但速度很高的存储器。CPU的速度远高于内存,当CPU直接从内存中存取数据时要等待一定时间周期,而Cache则可以保存CPU刚用过或循环使用的一部分数据,如果CPU需要再次使用该部分数据时可从Cache中直接调用,这样就避免了重复存取数据,减少了CPU的等待时间,因而提高了系统的效率。3sc6410含有两个cache,分别是数据cache和指令cache。 这么好的东西为什么要关闭呢?对呀,为什么呀?那是因为相对于s3c6410的启动引导文件,并不需要使用到他们,开启了使用不当反而会导致异常产生。那么如何来关闭他们呢?这又的操作协处理器p15了。查看arm架构参考手册协处理器相应章节,找到cache相关协处理器命令,如下:
协处理器的命令都是死命令,想干啥找到了直接用就可以,是不是很爽呢?这里只是让cache失效了,但是cache具体使能位和mmu使能位还没有设置,找到协处理器的控制寄存器(c1),其中有这么一句话,如下红框中:
协处理器p15的控制寄存器c1格式如下:
详细说明如下:
因此我们只要设置[2:0]位位0即可,顺便关闭字节对齐检查功能,代码如下: @ 关闭MMU和cache disable_mmu: mcr p15,0,r0,c7,c7,0 @ 使I/D cache全部失效 参考arm11内核手册:c7, Cache operations mrc p15,0,r0,c1,c0,0 @ 取出协处理器p15的控制寄存器(c1)内容,存放在r0中 参考arm11内核手册:c1, Control Register bic r0, r0, #0x00000007 @ 将r0的[2:0]设为0后存入r0 mcr p15,0,r0,c1,c0,0 @ 将r0的值传送到协处理器p15的控制寄存器(c1)中,完成mmu和cache配置 mov pc, lr @ 返回调用处继续往下执行未完,待续。。。
|