SylixOS ARM BSP 第二篇【startup.S】

此篇博客为 SylixOS ARM BSP 编写连载的第二篇,主要介绍 startup.S 文件具体实现。

startup.S 为 BSP 启动代码入口,通常由 bootloader 装载完 SylixOS 镜像后调用,下面以 S3C2440A 处理器为例,逐块介绍 startup.S 代码。

#ifndef ASSEMBLY
#define ASSEMBLY 1
#endif

此段代码告知后面引用的头文件,此文件为汇编程序。

#include <arch/assembler.h>

引用操作系统汇编头文件,此头文件根据编译器和平台类型,定义了很多编译器与平台相关的处理宏,可以统一不同平台,不同编译器之间对汇编语言关键字的差异。例如 FILE_BEGIN() 宏就定义在此文件,表示汇编语言文件起始。

#define UND_STACK_SIZE  0x00001000
#define ABT_STACK_SIZE  0x00001000
#define FIQ_STACK_SIZE  0x00001000
#define IRQ_STACK_SIZE  0x00001000
#define SVC_STACK_SIZE  0x00001000
#define SYS_STACK_SIZE  0x00001000

这段代码定义 ARM 各个模式下预设堆栈的大小,一般不需要调整。

IMPORT_LABEL(archIntEntry)
IMPORT_LABEL(archAbtEntry)
IMPORT_LABEL(archPreEntry)
IMPORT_LABEL(archUndEntry)
IMPORT_LABEL(archSwiEntry)
IMPORT_LABEL(sdramInit)
IMPORT_LABEL(targetInit)
IMPORT_LABEL(bspInit)

IMPORT_LABEL()表示此文件需要引用的外部符号,相当于 C 程序中 extern 关键字。

SECTION(.vector)

表示之下的所有代码或者文字池编译后存放在名为 .vertor 的节区中,链接器根据链接脚本,将此节设定为系统入口。

FUNC_DEF(vector)
    LDR     PC, resetEntry
    LDR     PC, undefineEntry
    LDR     PC, swiEntry
    LDR     PC, prefetchEntry
    LDR     PC, abortEntry
    LDR     PC, reserveEntry
    LDR     PC, irqEntry
    LDR     PC, fiqEntry
    FUNC_END()
    
FUNC_LABEL(resetEntry)
    .word   reset
FUNC_LABEL(undefineEntry)
    .word   archUndEntry
FUNC_LABEL(swiEntry)
    .word   archSwiEntry
FUNC_LABEL(prefetchEntry)
    .word   archPreEntry
FUNC_LABEL(abortEntry)
    .word   archAbtEntry
FUNC_LABEL(reserveEntry)
    .word   0
FUNC_LABEL(irqEntry)
    .word   archIntEntry
FUNC_LABEL(fiqEntry)
    .word   0

FUNC_DEF()表示定义一个函数,这里定义一个名为 vector 的函数,根据顺序,此函数将是 .vector 节区的入口函数。

FUNC_END()表示函数的结束。

这段代码是根据 ARM 向量规范编写的跳转表,进入不同的异常跳转到不同的地址,其中 ARM FIQ 快速中断这里并没有处理,说明 SylixOS 默认情况下并不接管 FIQ 异常,用户如果有需要可自行编写相关处理函数。

以上代码我们可以看出,系统复位向量最终跳转到 reset 函数(或者标号)处,接下来我们看看 reset 函数实现。

SECTION(.text)

与 SECTION(.vector)含义相同,表明以下代码或者文字池存放在 .text 节区中。这里需要说明的是一般一个程序至少分为三个节区(段)分别是代码段(.text)、数据段(.data)、清零段(.bss)。其中代码段里面存放的一般为程序代码和运行时不修改的表结构;数据段中主要存放有初值的全局变量;清零段存放着在运行主要程序之前需要清零操作的全局变量。链接器根据链接脚本来确定链接 target 各个节区(段)的内存布局情况。具体链接脚本的编写,我们放在之后的章节介绍。

FUNC_DEF(reset)
    LDR     R0 , =WTCON
    LDR     R1 , =0x0
    STR     R1 , [R0]

reset 函数首先关闭 2440 处理器内部看门狗,以防止启动的过程中被看门狗复位。这里扩展说两句,如果目标机为 SMP (对称多处理器)处理器,例如 ARM Cortex-A9 多核,所有的核程序入口都为 reset,所以在 reset 函数刚开始就需要判断此核为主核(primary)或者从核(secondary),系统启动时不同的核初始化顺序不同,但进入多任务状态后各个核之间不在有明确的分别。

    LDR     R0 , =0x31800000                   /*  内核高端地址                 */
    MSR     CPSR_c, #(UND32_MODE | DIS_INT)
    MOV     SP , R0
    SUB     R0 , R0, #UND_STACK_SIZE
    MSR     CPSR_c, #(ABT32_MODE | DIS_INT)
    MOV     SP , R0
    SUB     R0 , R0, #ABT_STACK_SIZE
    MSR     CPSR_c, #(IRQ32_MODE | DIS_INT)
    MOV     SP , R0
    SUB     R0 , R0, #IRQ_STACK_SIZE
    MSR     CPSR_c, #(FIQ32_MODE | DIS_INT)
    MOV     SP , R0
    SUB     R0 , R0, #FIQ_STACK_SIZE
    MSR     CPSR_c, #(SVC32_MODE | DIS_INT)
    MOV     SP , R0
    SUB     R0 , R0, #SVC_STACK_SIZE
    MSR     CPSR_c, #(SYS32_MODE | DIS_INT)
    MOV     SP , R0
    SUB     R0 , R0, #SYS_STACK_SIZE
    MSR     CPSR_c, #(SVC32_MODE | DIS_INT)

此段代码初始化 ARM 处理器各种模式下的堆栈,虽然 ARM 处理器既支持堆栈从高地址向低地址增长,又支持堆栈从低地址向高地址增长,但一般情况下使用高地址向低地址增长这种模式。所以我们将堆栈其实点定义在内核高地址位置,这个地址是根据 bspMap.h 设置的内存布局获得的,稍后会在 bspMap.h 里面详细介绍。

    BL      sdramInit
    BL      targetInit

初始完 ARM 堆后,我们调用两个子函数去初始化 2440 的 SDRAM 接口与 PPL 锁相环,当然针对不同的处理器,这里的代码也不尽相同,如果由 bootloader 引导,其实不需要这一部分代码,bootloader 已经初始化好了相关的参数。

    LDR     R1 , =_etext                    /*  -> ROM data end             */
    LDR     R2 , =_data                     /*  -> data start               */
    LDR     R3 , =_edata                    /*  -> end of data              */
1:
    CMP     R2 , R3                         /*  check if data to move       */
    LDRLO   R0 , [R1] , #4                  /*  copy it                     */
    STRLO   R0 , [R2] , #4
    BLO     1b                              /*  loop until done             */

这部分代码为一个区域拷贝函数,我们目前只需要知道这段代码是给数据段(.data)赋初值就可以了,因为这里涉及到链接脚本中关于装载段与运行段不相同时的处理支持,要完全说清楚,篇幅较大,需要读者自己根据链接脚本内容查阅相关知识。当然 RealCoder 会根据用户 BSP 向导中用户设置的相关参数,自动完成链接脚本的生成。

    MOV     R0 , #0                          /*  get a zero                  */
    LDR     R1 , =__bss_start                /*  -> bss start                */
    LDR     R2 , =__bss_end                  /*  -> bss end                  */
2:
    CMP     R1 , R2                          /*  check if data to clear      */
    STRLO   R0 , [R1],   #4                  /*  clear 4 bytes               */
    BLO     2b                               /*  loop until done             */

以上代码与数据段(.data)赋初值类似,这段代码是清零段(.bss)清零操作循环。

以上代码可以得出一个结论:代码段(.text)是由 bootloader 初始化的,数据段(.data)与清零段(.bss)是由 BSP 入口函数初始化的,当这三个段初始化完毕,我们就可以进入 bspInit() 函数开始正式初始化操作系统,调用 bspInit() 函数代码如下:

    LDR     R10, =bspInit
    MOV     LR , PC
    BX      R10

startup.S 文件最后有一句 FILE_END(),它是与 FILE_BEGIN() 语句对称使用,表明汇编程序结束。

(此篇完)

时间: 2024-10-15 23:54:11

SylixOS ARM BSP 第二篇【startup.S】的相关文章

SylixOS ARM BSP 第三篇【bspInit.c】

此篇博客为 SylixOS ARM BSP 编写连载的第三篇,主要介绍 bspInit.c 文件具体实现. bspInit.c 为 BSP 操作系统初始化部分代码,通常由 startup.S 初始完基本处理器参数后调用,下面以 S3C2440A 处理器为例,逐块介绍 bspInit.c 代码. SylixOS ARM BSP 第二篇中提到 startup.S 初始化完成会将会调用 bspInit() 函数,此函数用于初始化操作系统,并开始多任务调度. int bspInit (void) {  

转载:eclipse 搭建SSH项目(第二篇,有具体的项目例子)

原文地址:http://blog.csdn.net/yeohcooller/article/details/9316923 读博文前应该注意: 本文提纲:本文通过一个用户注册的实例讲解SSH的整合.创建Struts项目,整合Hibernate,整合Spring.最后总结如何熟练创建SSH项目. 仅是创建SSH项目,对于其他的扩展例如Struts的国际化,Hibernate的缓存优化,Spring的AOP等,本博文涉及不到.想学习更多的东西请搜索其他博文. 本项目的环境:Windows 8-64位

SylixOS网卡驱动调用篇

1. 开发环境 操作系统:SylixOS 编程环境:RealEvo-IDE3.1 硬件平台:IMX6Q实验箱 2. 技术实现 <SylixOS网卡驱动框架篇>里提过,网卡驱动的收发功能是通过netdev结构里的transmit和receive两个成员实现的.本篇文章将介绍SylixOS里的LWIP协议栈是如何调用底层网卡驱动里的这两个成员函数的. 2.1      网卡发送函数的调用 整个网卡驱动发送函数的调用关系如图 21所示. 网卡驱动在向SylixOS注册网卡驱动时,需要调用netdev

Android Metro风格的Launcher开发系列第二篇

前言: 各位小伙伴们请原谅我隔了这么久才开始写这一系列的第二篇博客,没办法忙新产品发布,好了废话不说了,先回顾一下:在我的上一篇博客Android Metro风格的Launcher开发系列第一篇写了如何配置Android开发环境,只是用文字和图片展示了开发Metro风格Launcher的初步设计和产品要求,这一篇文章将会从代码上讲解如何实现对应的UI效果,好了,评书开讲! Launcher主体框架实现: Launcher主体框架我选用的是大家所熟悉的ViewPager控件,因为ViewPager

[C++11新特性]第二篇

0.可变数量参数,可变函数模版,变长模版类 c++98可变数量参数 #include<cstdio> #include<cstdarg> double SumOfFloat(int count, ...) { va_list ap; double sum=0; va_start(ap,count); for(int i=0;i<count;i++) sum+=va_arg(ap,double); va_end(ap); return sum; } int main() { p

第二篇 基于微擎的模块开发—PHP

从陌生到如今能勉强完成第一个微网站模块的实现.也算是一个小小的进步,从设计数据库到,返回数据,前端模版渲染 每一点都是有点难度的.所以我想总结一下,我是如何实现一个微擎模块. 第一,首先得分析某个模块的想实现什么需求,根据需求设计合理的数据库结构. 第二,了解微擎的结构,运行流程,设计模块结构. 第三,重点就是site.php , 完成site.php 需要一定的php的编程能力, 第四,site.php 其中 通过 pdo 从数据库的获取我们想得到数据源. 微擎已封装其路由机制, doWeb

Python之路【第二篇】:Python基础(一)

Python之路[第二篇]:Python基础(一) 入门知识拾遗 一.作用域 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. 1 2 3 if 1==1:     name = 'wupeiqi' print  name 下面的结论对吗? 外层变量,可以被内层变量使用 内层变量,无法被外层变量使用 二.三元运算 1 result = 值1 if 条件 else 值2 如果条件为真:result = 值1如果条件为假:result = 值2 三.进制 二进制,01 八进

chromium浏览器开发系列第二篇:如何编译最新chromium源码

说一下为什么这么晚才发第二篇,上周和这周department的工作太多了,晚上都是十点半从公司出发,回家以后实在没有多余的精力去摸键盘了.所以请大家包涵! 上期回顾: chromium源码下载: 1.找个靠谱的vpn(我试过了,网上说的不用vpn拿代码的都不靠谱): 2.获取depot_tools,解压,设置环境变量; 3.gclient获取python和git,svn,设置环境变量: 4.fetch–nohooks chromium –nosvn=true 获取源码: 5.gclientsyn

第二篇 SQL Server代理作业步骤和子系统

本篇文章是SQL Server代理系列的第二篇,详细内容请参考原文. SQL Server代理作业由一系列的一个或多个作业步骤组成.一个作业步骤分配给一个特定的作业子系统(确定作业步骤去完成的工作).每个作业步骤运行于一个单独的安全上下文,尽管每个作业有一个所有者来决定谁可以修改作业.本篇主要关注组成SQL Server代理的作业步骤和子系统.快速回顾作业理解SQL Server代理作业的最佳方式是把相关联的 需要完成给定任务 的组件放在一个容器中.作业最主要的组件有作业步骤.计划.警告和通知.