S3C2440 Boot Loader引导代码功能简述

发布者:rho27最新更新时间:2021-10-14 来源: eefocus关键字:S3C2440  Boot  Loader 手机看文章 扫描二维码
随时随地手机看文章

【前言】开始学习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光初始化都要这样折腾,如果这个都折腾会了,后面的就慢慢来吧!

关键字:S3C2440  Boot  Loader 引用地址:S3C2440 Boot Loader引导代码功能简述

上一篇:s3c2440的Memory Controller与外设地址线错位连接分析
下一篇:s3c2440学习之路-001 汇编点亮led

推荐阅读最新更新时间:2024-11-17 12:22

内存接口概念
学习总结。 S3C2440 芯片内包含GPIO控制器,UART,I2C等控制器,CPU操作寄存器用来控制引脚输出或者输入,操作寄存器实际就是操作内存地址,因此引用了内存控制器。CPU 把地址发送给内存控制器,内存控制器根据地址,将地址发送给不同的模块,例如GPIO控制器,串口控制器。通常CPU发出的地址不会发送到片外设备。   但是内存类型设备是个例外,内存设备包括,内存,网卡,orflash等。CPU直接将地址信号和数据通过地址总线和数据总线发送给内存类设备。 以上类型设备都数据CPU统一编址(NAND FLASH 不参与CPU统一编址)。SDRAM、NOR flash,网卡共用地址总线,数据总线,为了
[单片机]
内存接口概念
s3c2440裸机-spi编程-2-OLED显示面板
1.OLED显示面板介绍 以QG-2864TMBEG01这款OLED为例,可见它支持Parallel/i2c/SPI这3种方式对它进行控制,这里仅对它进行SPI控制。它的product Specification见附件。 并行接口时序: SPI串行接口时序 Tr/Tf: 表示spi clk上升/下降延不能超过40ns Tclkl/Tclkh: 表示spi clk低/高电平持续至少20ns Tcycle: 表示spi clk一个时钟周期至少100ns Tdsw/Tdhw: 表示spi data的建立/持续时间至少15ms Tcss:片选建立时间至少20ns Tcsh:片选持续时间至少10ns Tas/Tah:
[单片机]
s3c2440裸板_时钟系统及定时器
若Fout = 200MHz则, Fout = 2 *m * Fin / (p * 2 ^ s) = 2 * (92 + 8) * 12MHz / (3 * 2 ^ 2) = 200MHz m = 100, MDIV = 92 p = 3, PDIV = 2 s = 2, SDIV = 2 #define S3C2440_MPLL_200MHZ ((0x5c 12) | (0x01 4) | (0x02)) CLKDIVN = 0x03; // FCLK:HCLK:PCLK=4:2:1, HDIVN=1,PDIVN=1 /* * * * * * * * * * * * *
[单片机]
<font color='red'>s3c2440</font>裸板_时钟系统及定时器
S3C2440,Linux,LCD驱动
到了神秘的LCD驱动了,信息还真有点胆怯,但是还是不得不走下去。对刚刚学习的linux驱动坐一下总结,毕竟是Linux内核当中的东东,而且是那么的繁琐。做一总结,等用笔记把学过东西几下来,这样就不会忘了。哈哈! 那就开始!!! 在编写裸机LCD程序的时候,首先就是硬件初始化操作。有一个寄存器当中存放了帧缓冲的起始地址。这个参数是非常重要的。当配置好硬件后,帧缓冲中的数据能够脱离CPU不停地将真缓冲当中的数据写入到LCD屏。如果我们要现实一个图片的话只需要将图片数据放到帧缓冲当中,这样就非常的方便了。 在linux当中,把整个LCD驱动分为两层:LCD帧缓冲区层和LCD硬件驱动层。LCD帧缓冲区层其实就是将内核中的一部分空间当作一个
[单片机]
s3c2440液晶屏驱动 (非内核自带) linux-4.1.24
对于,不想逐一检查内核自带驱动,想自己编写驱动。 1,make menuconfig 去掉 编译到内核,改为 M 编译为 模块(因为要用到里面的3个.ko 驱动) Device Drivers --- Graphics support --- Support for frame buffer devices --- M S3C2410 LCD framebuffer support 2,make uImage && make modules 生成新内核 和 模块文件 烧写新内核或使用 nfs bootm 使用编译为 M 模块的内核启动。 复制 3个 ko 文件到 文件系统,这里用的是 NFS 网络文件
[单片机]
ALSA声卡09_从零编写之参数设置_学习笔记
1、参数设置分析 (1)open: soc_pcm_open 依次调用cpu_dai, dma, codec_dai, machine的open或startup函数 只在dma的open函数里添加参数相关的代码 (2)SNDRV_PCM_IOCTL_HW_PARAMS: soc_pcm_hw_params 依次调用machine,codec_dai,cpu_dai,platform(dma)的hw_params函数 在uda1341.c, s3c2440-iis.c里实现hw_params函数(把裸板程序里面的相关代码移过来) (s3c2440-dma.c 主要涉及数据传输,在下一节实现hw_params函数) (3)
[单片机]
ALSA声卡09_从零编写之参数设置_学习笔记
S3C2440之SDRAM
前言 本文记录的是内存控制器如何访问SDRAM 测试程序:如果能成功访问SDRAM,就把LED点亮 一、思维导图 二、代码 关键代码 init.c(示例): #include S3C2440.h void sdram_init(void) { BWSCON = 0x22000000; BANKCON6 = 0x18001; BANKCON7 = 0x18001; REFRESH = 0x8404F5; BANKSIZE = 0xb1; MRSRB6 = 0x20; MRSRB7 = 0x20; } int sdram_test(void) { volat
[单片机]
<font color='red'>S3C2440</font>之SDRAM
基于S3C2440的MiniGUI移植
随着嵌入式系统的迅速发展,图形用户界面(Graphic User Interface,GUI)需求越来越明显。MiniGUI是面向实时嵌入式系统的轻量级图形用户界面支持系统,以轻型、占用资源少、高性能、可配置等特点广泛应用于通信、医疗、工控、电子、机顶盒、多媒体等领域。本文正是针对这种需求,介绍如何在S3C240上移植MiniGUI的全过程。 1 图形用户界面MiniGUI MiniGUI是由原清华大学教师魏永明主持开发的轻量级图形系统,遵循GPL公约。所支持的操作系统已不仅仅限于Linux,它还可以在μCli nux,μC/OS-Ⅱ,eCos和VxWorks等系统上运行。已验证的硬件平台包括Intelx86,ARM(ARM
[单片机]
基于<font color='red'>S3C2440</font>的MiniGUI移植

推荐帖子

关于输出电压保持电路的设计
小弟要用STM32单片机输出可控电压,但是在电压切换(改变电压)的时候,中间会出现电压信号下降或消失的情况,这在单片机中不可改变,因此想用外围电路解决这个问题。比如:我第一次输出V1作用在负载上面,然后需要输出V2,怎样才能使V2输出之前,V1始终作用在负载上而不消失?谢谢各位大神了。。。小弟也尝试过用电容的充放电原理,但是我要输出的电压是-3.3V到3.3v,怎样才能确电压保持时间一直是0.02s左右呢?关于输出电压保持电路的设计楼主没有把问题叙述清楚:是有多路各不相同的输出电
cxsy12300 模拟电子
放大电路频率响应的一般分析方法
为了简化计算,放大电路的频率响应分析一般采用分频区分析的方法,即按低频区、中频区和高频区分别进行。在每个频区分析时,先根据其工作特点抓住影响该频区的主要参数对电路进行简化,并在此基础上求得本区的频率响应。最后,将三个频区的结果综合起来就得到电路的全频率的响应。单级共射放大电路的低频响应1、画出低频小信号等效电路在低频区应将开路,而考虑C的作用,可画出低频小信号等效电路如图所示。2、频响分析对此式进行整理,得
qinkaiabc 模拟电子
protel问题
刚开始学这个东西,麻烦各位高手指点下:原理图中我想在一根导线的两头分别定义不同的网络标号,但是电气检查的时候总是提示错误,我看英文的意思应该是重复定义或者是多重定义,怎么弄能电气检查不报错呢?protel问题是重复定义,关了电气检查中这一项就可以了。谢谢管理员里了:D我有一个问题:为什么每打开一个protel文件,关闭后都会生成好多垃圾文件啊??请各位大侠指导!
logicshi PCB设计
请教:下面的图表示什么意思
谢谢!请教:下面的图表示什么意思伏安特性测试,但图具体怎么看,我也不知道了回复:请教:下面的图表示什么意思大体上总算明白了,就是还有一个问题图上是测试数据:电压输入从0V不断调高电压到48V,一边测试输出的电压和电流,如果坐标点在那些绿线和红线之中,就是不希望的;如果坐标点全部落在红线之外就是所期望达到的正常工作区域实际那条粗黑线有很多点落在了红线的区域之类,所以需要再进行修改调整电路刚才跟交给我这张数据测试表的人讨论了一下,听他意思,他是想模拟电源启动过程(实际工作电压为12V
quben 模拟电子
可调色温LED灯具电路疑问
各位大侠!好!LED可调色温次级输出电路,图中红色圈起的部分是起什么作用?请指教!谢谢可调色温LED灯具电路疑问 严重怀疑此电路的正确性。三支MOS管门极均联接到漏极,如果输入电压(C2两端电压)够高,那么三支MOS管均导通,不起什么作用。 管子上面的丝印是AOH,不知是什么管,网上查了下是MOS管三个PMOS管并联有扩流的意思还有,不知道前面供电方式,疑似有防反的作用 AC120V供电的,出口北美.用的的iWATT3689-00
wzk198005 模拟电子
显示屏背光闪烁是什么原因导致的?
我的一个设计里使用SY7200作为LCD背光的升压芯片,电压从7V升压至23V左右,背光电流50mA左右。我使用下图的设计,电感1.6A额定电流,二极管1A电流。现在极少的时候会在上电后观察到显示屏有闪烁的现象,闪烁不是很明显。有时通一会儿电就不闪烁了,所以用示波器很难观察到电压变化。这种情况有遇到过的吗?有可能是什么原因导致的呢?显示屏背光闪烁是什么原因导致的?会不会输入端就不稳定?元件的参数你没标上啊。。最好用示波器看输入和输出的电压波动。。这个要看具体的情况,
littleshrimp 国产芯片交流
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved