u-boot之ARM920T的start.S分析

发布时间:2024-08-29  

cpu/arm920t/start.S程序步骤大致有以下几个


1、设置中断向量表


2、设置CPU模式为SVC32 mode并且关闭IRQ与FIQ中断


3、关闭看门狗


4、屏蔽所有中断


5、判断程序是否在RAM中运行如果不是的话则先关闭MMU再则需要初始化RAM。


6、设置堆栈准备在C函数中运行了


7、 跳转到C函数clock_init初始化系统时钟


8、跳转到C函数CopyCode2Ram将代码拷贝到RAM中


9、清零BSS段


10、跳转到_start_armboot运行,此时代码已经在RAM中运行了


11、IRQ中断与FIQ中断发生后的上下文处理


1、cpu/arm920t/start.S文件中存放着S3C2440芯片(内核是ARM920T)的异常向量表(即异常入口地址),从S3C2440芯片手册得知每当板子重新上电即芯片复位后pc的值总是指向0,所以在链接时存放在最前面的文件肯定是cpu/arm920t/start.S。


.globl _start

_start:    b       reset                                   //复位异常地址

    ldr    pc, _undefined_instruction             //未定义异常地址

    ldr    pc, _software_interrupt                 //软件中断异常地址

    ldr    pc, _prefetch_abort                      //预取指异常地址

    ldr    pc, _data_abort                           //数据异常地址

    ldr    pc, _not_used                             //保留

    ldr    pc, _irq                                      //IRQ中断异常地址

    ldr    pc, _fiq                         //FIQ快速中断异常地址


_undefined_instruction:    .word undefined_instruction//0x20地址处存放着未定义异常跳转地址

_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 fiq


    .balignl 16,0xdeadbeef                               //0x3c地址存放一个固定的标志表明前面的数据为中断向量表占用


2、当复位异常出现后,程序执行b reset后设置CPU模式为SVC32 mode并且关闭IRQ与FIQ中断,b指令为位置无关码,它的寻址方式是先找到reset程序段所在地址然后计算出当前程序地址与reset段地址的偏移然后根据偏移值跳转,这么做的目的是因为:假设开发板通过nand启动的,那么一上电后硬件自动将nand内的前4K程序拷贝到s3c2440内部的ram中运行程序,这段程序的地址与链接地址不一致,当程序运行地址与链接地址不一致时只能使用位置无关码进行跳转


reset:

    /*

     * set the cpu to SVC32 mode //设置cpsr寄存器使CPU进入系统模式,并且关闭FIQ与IRQ

     */

    mrs    r0,cpsr

    bic    r0,r0,#0x1f

    orr    r0,r0,#0xd3

    msr    cpsr,r0



3、关闭看门狗


#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

    ldr     r0, =pWTCON//pWTCON为看门狗寄存器地址,全部清0即关闭看门狗

    mov     r1, #0x0

    str     r1, [r0]//关闭看门狗

 


4、屏蔽所有中断,S3C2440的中断控制器可以控制多个中断源,可以设置中断优先级。通过选出优先级最高的中断源送到ARM920T的IRQ或FIQ异常,最后通过IRQ或FIQ异常响应相应的中断源


    /*

     * mask all IRQs by setting all bits in the INTMR - default

     */

    mov    r1, #0xffffffff

    ldr    r0, =INTMSK               //INTMSK为中断控制寄存器

    str    r1, [r0]                      //屏蔽所有without sub中断源

# if defined(CONFIG_S3C2410)

    ldr    r1, =0x3ff

    ldr    r0, =INTSUBMSK        //INTSUBMSK为中断控制寄存器

    str    r1, [r0]                     //屏蔽所有with sub中断源 

# endif


5、判断程序是否在RAM中运行如果不是的话则先关闭MMU再则需要初始化RAM。下面代码注释引自博客http://www.cnblogs.com/lifexy/p/7309791.html


#ifndef CONFIG_SKIP_LOWLEVEL_INIT   //这个在100ask24x0里未定义

    adr    r0, _start        /* r0 <- current position of code   */

    ldr    r1, _TEXT_BASE        /* test if we run from flash or RAM */

    cmp     r0, r1                  /* don't reloc during debug         */

    blne    cpu_init_crit            //未在ram中运行,所以要初始化ram


cpu_init_crit:


mov    r0, #0

mcr    p15, 0, r0, c7, c7, 0    //关闭ICaches(指令缓存,关闭是为了降低MMU查表带来的开销)和DCaches(数据缓存,DCaches使用的是虚拟地址,开启MMU之前必须关闭)

mcr    p15, 0, r0, c8, c7, 0    //使无效整个数据TLB和指令TLB(TLB就是负责将虚拟内存地址翻译成实际的物理内存地址)


mrc    p15, 0, r0, c1, c0, 0

bic    r0, r0, #0x00002300    @ clear bits 13, 9:8 (--V- --RS) //bit8:系统不保护,bit9:ROM不保护,bit13:设置中断向量表的位置为0x0~0x1c,即异常模式基地址为0X0

bic    r0, r0, #0x00000087    @ clear bits 7, 2:0 (B--- -CAM) //bit0~2:禁止MMU,禁止地址对齐检查,禁止数据Cache.bit7:设为小端模式

orr    r0, r0, #0x00000002    @ set bit 2 (A) Align //bit2:开启数据Cache

orr    r0, r0, #0x00001000    @ set bit 12 (I) I-Cache //bit12:开启指令Cache

mcr    p15, 0, r0, c1, c0, 0

/*

mcr/mrc:

Caches:是一种高速缓存存储器,用于保存CPU频繁使用的数据。在使用Cache技术的处理器上,当一条指令要访问内存的数据时,

首先查询cache缓存中是否有数据以及数据是否过期,如果数据未过期则从cache读出数据。处理器会定期回写cache中的数据到内存。

根据程序的局部性原理,使用cache后可以大大加快处理器访问内存数据的速度。

其中DCaches和ICaches分别用来存放数据和执行这些数据的指令


TLB:就是负责将虚拟内存地址翻译成实际的物理内存地址,TLB中存放了一些页表文件,文件中记录了虚拟地址和物理地址的映射关系。

当应用程序访问一个虚拟地址的时候,会从TLB中查询出对应的物理地址,然后访问物理地址。TLB通常是一个分层结构,

使用与Cache类似的原理。处理器使用一定的算法把最常用的页表放在最先访问的层次。

这里禁用MMU,是方便后面直接使用物理地址来设置控制寄存器

*/

mov    ip, lr //临时保存当前子程序返回地址,因为接下来执行bl会覆盖当前返回地址.

bl    lowlevel_init //跳转到lowlevel_init(位于u-boot-1.1.6/board/100ask24x0/lowlevel_init.S)

mov    lr, ip //恢复当前返回地址

mov    pc, lr //退出


.globl lowlevel_init

lowlevel_init://配置存储控制器  by andy

    /* memory control configuration */

    /* make r0 relative the current location so that it */

    /* reads SMRDATA out of FLASH rather than memory ! */

    ldr     r0, =SMRDATA

    ldr    r1, _TEXT_BASE

    sub    r0, r0, r1 //求出SMRDATA实际位于的地址,nand启动的话起始是位于0地址的,而不是链接地址

    ldr    r1, =BWSCON    /* Bus Width Status Controller */

    add     r2, r0, #13*4

0:

    ldr     r3, [r0], #4

    str     r3, [r1], #4

    cmp     r2, r0

    bne     0b


    /* everything is fine now */

    mov    pc, lr


    .ltorg

/* the literal pools origin */


SMRDATA:

    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) 

    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))

    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))

    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))

    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))

    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))

    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))

    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))

    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))

    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)

    .word 0xb1

    .word 0x30

    .word 0x30


6、设置堆栈准备在C函数中运行了。在SDRAM中,代码段位于0x3f80000以上,sp=_TEXT_BASE-CFG_MALLOC_LEN-CFG_GBL_DATA_SIZE-CONFIG_STACKSIZE_IRQ-CONFIG_STACKSIZE_FIQ-12


    /* Set up the stack       */ //设置堆栈需要放到前面,因为clock_init是C函数

stack_setup:

    ldr    r0, _TEXT_BASE        /* upper 128 KiB: relocated uboot   *///_TEXT_BASE之前为代码段

    sub    r0, r0, #CFG_MALLOC_LEN    /* malloc area                      *///减去堆区

    sub    r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        *///减去全局变量区

#endif


#ifdef CONFIG_USE_IRQ

    sub    r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)//减去IRQ与FIQ占用的栈区

#endif

    sub    sp, r0, #12        /* leave 3 words for abort-stack    *///再预留12字节sp为堆栈寄存器,堆栈为向下增长

7、跳转到C函数clock_init初始化系统时钟bl   clock_init,clock_init具体代码如下:代码注释引自博客http://www.cnblogs.com/lifexy/p/7309791.html


void clock_init(void)

{

//时钟的寄存器地址只能在函数内部定义,因为代码还不在链接地址处运行

S3C24X0_CLOCK_POWER *clk_power = (S3C24X0_CLOCK_POWER *)0x4C000000; //定义一个S3C24X0_CLOCK_POWER型结构体指针,clk_power->LOCKTIME=0x4C000000


if (isS3C2410) //isS3C2410为0,执行else

{... ...}

else

{

/* FCLK:HCLK:PCLK = 1:4:8 */

clk_power->CLKDIVN = S3C2440_CLKDIV; //S3C2440_CLKDIV=0X05


/* change to asynchronous bus mod *///C语言内嵌汇编

__asm__( 'mrc p15, 0, r1, c1, c0, 0n' /* read ctrl register */ 

'orr r1, r1, #0xc0000000n' //使其从快总线模式改变为异步总线模式,在2440手册上看到

'mcr p15, 0, r1, c1, c0, 0n' /* write ctrl register */ 

:::'r1' //

);


/* to reduce PLL lock time, adjust the LOCKTIME register */

clk_power->LOCKTIME = 0xFFFFFFFF; //PLL 锁定时间计数寄存器


/* configure UPLL */

clk_power->UPLLCON = S3C2440_UPLL_48MHZ; //UCLK=48Mhz


/* some delay between MPLL and UPLL */

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>