历史上的今天
返回首页

历史上的今天

今天是:2024年09月08日(星期日)

2021年09月08日 | S3C2440 Boot Loader引导代码功能简述

2021-09-08 来源:eefocus

【前言】开始学习ARM的时候,基本上都要从裸机编程开始。为了减低入门的门槛,很多时候只要修改模板里的主函数main.c,可是,久而久之,就会产生些疑问,问什么下载了这些C代码编译链接生成出来的BIN就能在ARM上跑了呢?原因就在于,有几个文件已经不声不响的帮我们提前干了很多的事,而这些事C语言是干不了的,只能由汇编完成,美其名曰:ARM汇编引导代码。其实不光“裸奔”需要,Boot Loader也同样需要。那么到底这些汇编帮我干了些什么呢?笔者就结合S3C2440的Boot Loader引导代码简单分析整理下。


【一】变量及相关宏定义

开始首先用GET(相当于C语言里的#include)伪指令包含进来了三个头文件option.inc、memcfg.inc、2440addr.inc,其中option.inc里定义芯片相关的配置,memcfg.inc里定义存储器配置,2440addr.inc里定义了寄存器符号。


USERMODE    EQU 0x10


FIQMODE      EQU0x11


IRQMODE      EQU0x12


SVCMODE      EQU0x13


ABORTMODE   EQU 0x17


UNDEFMODE   EQU 0x1b


MODEMASK    EQU 0x1f 


NOINT         EQU0xc0


上面的几行进行了一些处理器模式的定义,下面定义了一些各模式下的常量,等到了【四】这一块再详细说。


UserStack   EQU(_STACK_BASEADDRESS-0x3800);0x33ff4800 ~  


SVCStack    EQU(_STACK_BASEADDRESS-0x2800);0x33ff5800 ~


UndefStack  EQU (_STACK_BASEADDRESS-0x2400);0x33ff5c00 ~


AbortStack  EQU (_STACK_BASEADDRESS-0x2000);0x33ff6000 ~


IRQStack    EQU(_STACK_BASEADDRESS-0x1000);0x33ff7000 ~


FIQStack    EQU(_STACK_BASEADDRESS-0x0);0x33ff8000 ~


注:_STACK_BASEADDRESS在option.inc中有相关定义


接下来的定义要到最后才能用到,THUMBCODE作为全局变量,其实就是一个指示的作用,在跳转到main前进行模式的切换。


下面的宏定义可能就不太好理解了,这个是一个中断跳转的工具,到【二】这再解释。


MACRO


$HandlerLabel HANDLER $HandleLabel


$HandlerLabel        ;标号


sub sp,sp,#4          ;(1)减少sp(用于存放转跳地址)


stmfd sp!,{r0}         ;(2)把工作寄存器压入栈


ldr     r0,=$HandleLabel   ;将HandleXXX的址址放入r0


ldr     r0,[r0]              ;把HandleXXX所指向的内容(也就是中断程序的入口)放入r0


str     r0,[sp,#4]            ;(3)把中断服务程序(ISR)压入栈


ldmfd   sp!,{r0,pc} ;(4)用出栈方式恢复r0原值和为pc设定新值(即完成了到ISR的转跳)


MEND


还有一些,留到后面用到的时候再说。


【二】中断向量表以及其相关跳转设置

在上述定义完成之后就算真正意义来到了函数的入口处,这里处理的比较复杂,会有大小


端的处理,因为对我们理解引导代码没多少作用,暂且将其简化处理掉。省略这些之后,其实入口就是这几行代码:


b ResetHandler   ;上电复位中断;0x00


b HandlerUndef   ;handlerfor Undefined mode  ;0x04


b HandlerSWI     ;handler for SWI interrupt  ;0x08


b HandlerPabort ;handler for PAbort    ;0x0c


b HandlerDabort ;handler for DAbort    ;0x10


b .                ;其实是个死循环   ;0x14


b HandlerIRQ     ;handler for IRQ interrupt  ;0x18


b HandlerFIQ      ;handler for FIQ interrupt  ;0x1c


这就是我们有名的中断向量表!中断向量表必须位于启动代码的开始部分连续8*4字节的连续空间,它是用户程序与启动代码之间以及启动代码的各部分之间联系的纽带。它由一个一个的跳转函数组成,它就象一个普通的散转函数,只不过散转的过程中有硬件机制参与,当系统发生异常时,ARM 处理器会通过硬件机制强制将PC 指针指向中断向量表中对应的异常跳转函数存储的地址,然后程序会跳转到相应的中断服务程序去执行。因为我们开机的第一个中断是上电复位,所以进来之后首先是跳转到ResetHandler中断函数里去进行一些必要的系统设置,故在0x00处就是bResetHandler。


对于ARM的中断,其实有两种模式(可通过相关寄存器设置):向量中断模式和普通中断模式。简单的区分这两个就是:对于向量中断模式,当中断发生时,CPU会跳转到向量表中相应中断类型的表项,直接把中断服务例程的起始地址送到PC,这个有个优点就是速度快;对于普通中断模式,在跳转到中断向量表之后还要进行一次跳转查询,最红由返回ISR的最红中断处理函数的地址给PC,现在就可以说说【一】中宏定义$HandlerLabel HANDLER $HandleLabel的作用了。这个宏是用于第一次查表过程的实现中断向量的重定向,在_ISR_STARTADDRESS里定义的第一级中断向量表是采用型如Handle###的方式的,而在程序的开始处采用的是b Handler###的方式,在这里Handler###就是通过HANDLER这个宏和Handle###建立联系的.所以在后面其实还有一段初始化程序作为宏展开。


HandlerFIQ      HANDLER HandleFIQ


HandlerIRQ      HANDLER HandleIRQ


HandlerUndef    HANDLER HandleUndef


HandlerSWI      HANDLER HandleSWI


HandlerDabort   HANDLER HandleDabort


HandlerPabort   HANDLER HandlePabort


这种方式的优点就是正真定义的向量数据在内存空间里,而不是在ENTRY处的ROM空间里,这样就可以在程序里灵活的改动向量的数据了.这段程序用于把中断服务程序的首地址装载到pc中,也可以称之为“加载程序”。


接着跳转那一块继续说,因为外部中断几乎都是通过IRQ引入的(其实FIQ理论上也可以,但是在linux几乎用不到),于是便跳到了HandleIRQ,但是此时HandleIRQ又是多少呢,在程序的下面还有一段也必须拿上来说:


ldr r0,=HandleIRQ        ;This routine is needed


ldr r1,=IsrIRQ             ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c


str r1,[r0]


可见,HandleIRQ和IsrIRQ其实等价了!于是可以把IsrIRQ处的处理函数拿来分析一下:


IsrIRQ


sub sp,sp,#4            ;给PC寄存器保留 reserved for PC


stmfd sp!,{r8-r9}              ;把r8-r9压入栈


;把INTOFFSET的地址装入r9, INTOFFSET是一个内部的寄存器,存着中断的偏移


ldr r9,=INTOFFSET


ldr r9,[r9]                  ;I_ISR


ldr r8,=HandleEINT0    ;这就是我们第二个中断向量表的入口的,先装入r8


add r8,r8,r9,lsl #2  ;地址对齐,每个中断向量占4个字节,即isr = IvectTable + Offeset * 4


ldr r8,[r8]    ;装入中断服务程序的入口


str r8,[sp,#8]   ;把入口也入栈,准备用旧招


ldmfd sp!,{r8-r9,pc} ;弹出栈,顺便把r8弹出到PC了,跳转成功!


 


【三】初始化硬件

终于可以开始对硬件真正的干涉了,ARM要能形成一个可以供C语言工作的环境,还要


要干下面的几件事:


1、 关看门狗,看门狗是用来解决软件崩溃的,这里不需要


ldr r0,=WTCON     


ldr r1,=0x0        


str r1,[r0]


2、关中断,引导代码里不需要处理中断事件,除了上电复位中断其它都交给C的主函数完成


ldr r0,=INTMSK


ldr r1,=0xffffffff 


str r1,[r0]


3、关子中断,同上


ldr r0,=INTSUBMSK


ldr r1,=0x7fff  


str r1,[r0]


4、减少PLL的lock time,调整LOCKTIME寄存器


ldr r0,=LOCKTIME


ldr r1,=0xffffff


str r1,[r0]


5、设定PLL,这个直接关系到板子的快慢,不过也不是越快越好,除了要考虑功耗外还要满足下面的公式:


Fpllo=(m*Fin)/(p*2^s)


m=MDIV+8,p=PDIV+2,s=SDIV(1<=P<=62, 1<=M<=248)


Fpllo必须大于200Mhz小于600Mhz


Fpllo*2^s必须小于1.2GHz


PLLCON设定中的M_DIV P_DIV S_DIV是取自option.inc中的


6、设置系统存储寄存器,其中SMRDATA在程序段的后面有详细描述,这里知道作用就好


adrl r0, SMRDATA 


ldr r1,=BWSCON  ;BWSCON Address


add r2, r0, #52  ;SMRDATA数据的结束地址,共有52字节的数据


0


ldr r3, [r0], #4


str r3, [r1], #4


cmp r2, r0


bne %B0  ;%表示搜索,B表示反向-back(F表示向前-forward),0为局部标号(0~99)


 


【四】 初始化堆栈

ARM 有7 种模式,用户模式,快速中断模式,中断模式,管理模式,中止模式,未定义模式和系统模式。系统堆栈的初始化主要是给各个处理器模式分配堆栈空间。堆栈是为中断或程序跳转服务的,当发生中断或程序跳转时,需要将当前处理器的状态及一些参数保持在堆栈中,当中断处理完毕以后或程序执行完后返回时,再将堆栈保存的现场数据进行恢复,以保证原来的程序正确运行。在【一】中已经提到了一些与堆栈有关的变量定义。可以这样简单说,堆栈的初始化分为两个步骤:1、指定堆栈的位置和大小,这些在【一】中已经完成了;2、将各个模式下的堆栈指针指向相应的栈,下面做的就是这个工作。


InitStacks


mrs r0,cpsr


bic r0,r0,#MODEMASK


orr r1,r0,#UNDEFMODE|NOINT


msr cpsr_cxsf,r1  ;UndefMode


ldr sp,=UndefStack  ; UndefStack=0x33FF_5C00


orr r1,r0,#ABORTMODE|NOINT


msr cpsr_cxsf,r1  ;AbortMode


ldr sp,=AbortStack ; AbortStack=0x33FF_6000


orr r1,r0,#IRQMODE|NOINT


msr cpsr_cxsf,r1  ;IRQMode


ldr sp,=IRQStack  ; IRQStack=0x33FF_7000


orr r1,r0,#FIQMODE|NOINT


msr cpsr_cxsf,r1 ;FIQMode


ldr sp,=FIQStack ; FIQStack=0x33FF_8000


bic r0,r0,#MODEMASK|NOINT


orr r1,r0,#SVCMODE


msr cpsr_cxsf,r1 ;SVCMode


ldr sp,=SVCStack  ; SVCStack=0x33FF_5800


注:仔细看看发现没有初始化user模式下的堆栈,为什么呢?很明显嘛,你一开始就运行在了user模式下了!


 


 


【五】C主函数接管前的数据搬移及入口设定

其实一直还有个东西没说,这个在进入代码段前就定义了,我提到了,后面用到会详细说,


现在是时候了。在【一】时用IMPORT伪指令引入了|Image

RO

RO

Base|  |Image

RO

RO

Limit|...这些变量是通过ADS、RVDS,MDK等工具的工程设置里面设定的RO Base和RW Base设定的,这个应该有印象,可能很多人感觉这个没用,其实很有用呢!那为什么要引入这玩意呢,最简单的用处是可以根据它们拷贝自己,这些变量是编译器生成的。


RO,RW, ZI这三个段都保存在Flash中,但RW,ZI在Flash中的地址肯定不是程序运行时变量所存储的位置,因此我们的程序在初始化时应该把Flash中的RW,ZI拷贝到RAM的对应位置。一般情况下,我们可以利用编译器替我们实现这个操作。比如我们跳转到main()时,使用 b  __Main,编译器就会在__Main和Main之间插入一段汇编代码,来替我们完成RW,ZI段的初始化。 如果我们使用b  Main, 那么初始化工作要我们自己做。编译器会生成如下变量告诉我们RO,RW,ZI三个段应该位于什么位置,但是它并没有告诉我们RW,ZI在Flash中存储在什么位置,实际上RW,ZI在Flash中的位置就紧接着RO存储。


IMPORT  |Image

RO

RO

Base| ; Base of ROM code


IMPORT  |Image

RO

RO

Limit|  ; End of ROM code (=start of ROM data)


IMPORT  |Image

RW

RW

Base|   ; Baseof RAM to initialise


IMPORT  |Image

ZI

ZI

Base|   ; Base and limit of area


IMPORT  |Image

ZI

ZI

Limit| ; to zero initialize


在程序的最后,通过下面的代码就可以进入main()了。


[ :LNOT:THUMBCODE;ifthumbcode={false} bl main   L代表logic变量


     bl Main       ;Don't use main() because ......


     b .           ;注意小圆点         


 ]


 [ THUMBCODE        ;for start-up code for Thumb mode


     orr lr,pc,#1


     bx lr


     CODE16


     bl Main        ;Don't use main() because ......


     b .          ;注意小圆点


     CODE32


 ]


现在,就可以顺便回顾下【一】中提到的THUMBCODE了,这不就是一个指示的作用吗?!

【后记】总的来说,ARM光初始化都要这样折腾,如果这个都折腾会了,后面的就慢慢来吧!

推荐阅读

史海拾趣

Excellence Optoelectronics Inc公司的发展小趣事

随着LED市场的不断发展,EOI意识到只有不断拓展市场,才能谋求更大的发展空间。因此,公司开始积极开拓国际市场,参加各种国际展会和交流活动,与国际同行建立联系和合作。同时,EOI还注重与国内客户的沟通和合作,了解他们的需求,提供个性化的解决方案。这些努力使得EOI的市场份额不断扩大,公司的业务规模也实现了快速增长。

中环(Central)公司的发展小趣事

2019年,中环公司发布了210mm尺寸G12超大硅片“夸父”系列产品。这一创新产品凭借其高效、高质的特点,迅速获得了市场的认可。G12超大硅片的推出不仅引领了光伏材料的发展方向,也进一步巩固了中环在光伏领域的领先地位。

FRONTIER公司的发展小趣事

背景:虽然此处的FRONTIER可能指的是边疆通信公司(Frontier Communications),但为符合电子行业背景,我们假设其在农村宽带服务方面的创新。

发展故事:在21世纪初期,FRONTIER Communications认识到农村地区对于宽带服务的迫切需求,于是开始大规模投资农村宽带基础设施建设。通过引入先进的通信技术和设备,FRONTIER Communications成功地将高速互联网带到了偏远地区,极大地改善了当地居民的生活和工作条件。这一举措不仅赢得了市场的广泛赞誉,也为公司带来了稳定的收入来源和持续增长的动力。

DUBILIER公司的发展小趣事

作为一家领先的电子企业,DUBILIER公司深知其对社会和环境的影响。因此,公司积极履行社会责任,致力于推动可持续发展。公司采取了一系列环保措施,减少生产过程中的废弃物和污染物排放。同时,DUBILIER公司还积极参与公益事业,为社会做出积极贡献。这些努力不仅提升了公司的社会形象,还为公司赢得了更多的尊重和信任。

敦泰(FOCALTECH)公司的发展小趣事
首先检查电源插头是否牢固插入插座,然后用万用表测量插座是否有电。如果插座有电但冰箱仍不工作,可以检查冰箱内部的电源线路是否有断路或短路现象。注意,在检查电源线路时务必断电操作,以免发生触电事故。
璟德(ACX)公司的发展小趣事

多年来,璟德(ACX)凭借其卓越的技术实力和市场表现,荣获了多项荣誉和奖项。这些荣誉不仅是对公司过去努力的肯定,也为其未来的发展注入了新的动力。同时,璟德(ACX)始终坚持创新驱动的发展理念,不断投入研发,推出新产品和新技术,以满足市场的不断变化和客户的需求。

请注意,以上故事是基于现有资料和行业常识构建的,可能无法完全反映璟德(ACX)公司实际发展历程中的所有细节和复杂性。如需了解更多关于璟德(ACX)公司的故事和发展历程,建议查阅公司官方网站、相关新闻报道或行业研究报告。

问答坊 | AI 解惑

MCGS组态软件设计及其应用

一、引言     过去工业控制计算机系统的软件功能都靠软件人员编程实现。工作量大,软件通用性差,且易产生错误。随着工业控制要求的不断提高,专门用于工业控制的组态软件应运而生,它是一套功能齐全的组态生成工具软件,通用性强,而 ...…

查看全部问答>

ARM c程序的问题

_irqHandler PROC    1. STMFD   sp!,{r0-r4,r12,lr}    2. mov     r4,#0x80000000  //中断控制寄存器首地址(假设的)    3. ldr     r0,[r4,#0] ...…

查看全部问答>

给论坛的建议

我希望论坛能够在下载扣金币方面可以放松点,比如每次只扣一个或者在某个贴上下载只扣若干个就好了,因为有些文件确实比较大,要下好几个压缩文件才能行的,而每下一个就扣好几,很多人都有些不舍得,毕竟金币有比较难得,特别是新手就更麻烦了,我 ...…

查看全部问答>

wince 桌面快捷方式

请问: wince中的文件夹有没有后缀名? 我想把一个名为NandFlash的文件夹,放到桌面快捷方式, 我在WINCE500\\PLATFORM\\SMDK2440\\FILES创建了一个NandFlash.LNK文件,里面写上22#\\windows\\NandFlash 我又在Project.bib 里面加上 NandFlash ...…

查看全部问答>

PCI设备识别不正常

首先我的程序在某些主板上是可以正常工作的(具体型号我也搞不懂),设置的是从设备,内部只有从设备状态机。没有接入奇偶检验,仲裁和热插拔。 问题是在一些主板上发现设置为从设备时无法找到设备,设置为主设备可以找到,但是通过软件读取配置信 ...…

查看全部问答>

0

居然要全部下完才能都打开?这个有点不厚道…

查看全部问答>

LM3S程序求教

//————————————————头文件————————————————————#include \"inc/hw_ints.h\"//硬件中断#include \"inc/hw_memmap.h\"#include \"inc/hw_types.h\"//硬件类型#include \"driverlib/gpio.h\"//GPIO#include \"dri ...…

查看全部问答>

中断究竟是个怎么回事?

今天听单片机老师讲课讲中断,说分了好几种,被他弄得一头雾水,还是没明白他说的中断都有什么,怎么配置,查了查2553手册,愣是没找到讲解终端的地方,球大神指点123!不胜感激。…

查看全部问答>

DCDC电源中的电流检测

文章介绍了7中电流检测的办法,可以作为电流检测的入门读物 …

查看全部问答>