当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]任何运行在实际硬件上的嵌入式应用程序,都必须在启动时实现一些基本的系统初始化。本节将对此予以详细讨论。

13.5 复位和初始化

任何运行在实际硬件上的嵌入式应用程序,都必须在启动时实现一些基本的系统初始化。本节将对此予以详细讨论。

13.5.1 初始化序列

图13.14显示了一个适用于ARM嵌入式系统的初始化序列。

图13.14 ARM嵌入式系统的初始化序列

系统启动时立即执行复位处理程序,然后进入$Sub$$main()的代码执行。

复位处理程序是用汇编语言编写的代码块,它在系统复位时执行,完成系统初始化操作。对于具有局部存储器的内核,如Caches、紧密藕荷存储器(TCM)、存储管理单元(MMU)和存储器保护单元(MPU)等,在初始化过程这一阶段完成必要的配置。复位处理程序在执行之后,通常跳转到__main以开始C库的初始化序列。

13.5.2 向量表

所有的ARM系统都有一个向量表(vector table)。向量表不是初始化序列的一部分,但是对每个要处理的异常,它必须存在。这些地址通常包含以下形式的跳转指令。

· B<address>:该条指令实现了相对于pc的跳转

· LDR pc,[pc,offset]:这条指令将异常处理程序的入口地址从存储器装载到pc。该地址是一个32位的绝对地址。由于有额外的存储器访问,装载跳转地址会使分支跳转到特定处理程序,给系统执行带来延时。不过,可以使用这种方法跳转到存储空间内的任意地址。

· MOV pc,#immediate:将一个立即数复制到pc。使用该指令可以跨越整个地址空间,但是受到地址对齐问题的限制。这个地址必须由8位立即数循环右移偶数次得到。

另外,也可以在向量表中使用其他类型的指令。例如,FIQ处理程序可以从地址0x1c处开始执行。因为它位于向量表的最后,这样FIQ处理程序就可以不用跳转,立即从FIQ向量地址处开始执行。

下面的例子显示了一个使用LDR指令的向量表装载过程。

;**********************************

;* VECTOR TABLE *

;**********************************

AREA vectors, CODE

ENTRY

; 定义标准的ARM向量表

INT_Vectors

LDR PC, INT_Reset_Addr

LDR PC, INT_Undef_Addr

LDR PC, INT_Software_Addr

LDR PC, INT_Prefetch_Addr

LDR PC, INT_Data_Addr

LDR PC, INT_Reserved_Addr

LDR PC, INT_IRQ_Addr

LDR PC, INT_FIQ_Addr

在向量表的入口处要有ENTRY标识。该标识通知链接程序该代码是一个可能的入口点,因而在链接时,不能被清除。

13.5.3 ROM/RAM重映射

启动时,0x0处必须要有一条有效指令,因此,复位时0x0000地址必须为非易失性存储器,如ROM或FLASH。

注意

有些系统是从0xffff0000处开始执行的,对于这样的系统,地址0xffff0000处必须为非易失性存储器。

可以将ROM定位在0x0处。但是,这样配置有几个缺点。首先ROM存取速度通常较RAM要慢,当跳转到异常处理程序时,系统性能可能会大受影响。其次,将向量表放于ROM中,运行时不能修改。

存储器地址重映射(Memory Remap)是当前很多先进控制器所具有的功能。所谓地址重映射就是可以通过软件配置来改变存储器物理地址的一种机制或方法。

当一段程序对运行自己得存储器进行重映射时,需要特别注意保证程序执行流程在重映射前后的承接关系。实现重映射的关键就是要使程序指针在remap以后能继续往下得到正确的指令。本书中介绍两种实现重映射的机制,不同的系统可能会有多种灵活的remap方案,用户在具体实现时要具体分析。

1.先搬移后映射(Remap after Copy)

图13.15显示一种典型的存储器地址重映射情况。

图13.15 ROM/RAM重映射(1)

原来RAM和ROM各有自己的地址,进行重映射以后RAM和ROM的地址都发生了变化。这种情况下,可以采用以下方案。

① 上电后,从0x0地址的ROM开始往下执行。

② 根据映射前的地址,对RAM进行必要的代码和数据拷贝。

③ 拷贝完后,进行remap操作。

④ 因为RAM在remap前准备好了内容,使得PC指针能继续在RAM里取到正确的指令。

2.先映射后搬移(Copy after Remap)

系统上电后的缺省状态是0x0地址上放有ROM。这块ROM有两个地址:从0起始和从0x10000起始,里面存储了初始化代码。当进行地址remap以后,从0x0起始的地址被定向到RAM上,ROM则只保留有惟一的从0x10000起始的地址。

如果存储在ROM里的复位异常处理程序(Reset-Handler)一直在0x0~0x4000的地址上运行,则当执行完remap以后,下面的指令将从RAM里预取,这必然会导致程序执行流程的中断。根据系统特点,可以用下面的办法来解决这个问题。

① 上电后系统从0x0地址开始自动执行,设计跳转指令在remap发生前使PC指针指向0x10000开始的ROM地址中去,因为不同地址指向的是同一块ROM,所有程序能够顺利执行。

② 这时候0x0~0x4000的地址空间空闲,不被程序引用,执行remap后把RAM引进。因为程序一直在0x10000起始的ROM空间里运行,remap对运行流程没有任何影响。

③ 通过在ROM里运行的程序,对RAM进行相应的代码和数据拷贝,完成应用程序运行的初始化。

图13.16显示了ROM和RAM重映射的第二种解决方案。

图13.16 ROM/RAM重映射(2)

该ROM与RAM地址重映射的方法可以应用于任何具有ROM/RAM重映射机制的平台,但是内存重映射的地址根据具体平台的不同而不同。

图13.16显示的地址重映射例子中,第一条指令实现从ROM临时地址(0x0地址)到实际ROM的跳转。然后,控制寄存器的重映射位,清除ROM的临时地址设置。该代码通常在系统复位后立即执行。重新映射必须在执行C库初始化代码前完成。

在具有MMU的系统中,可通过在系统启动时配置MMU来实现重映射。

下面的例子显示了在ARM的Integrator开发板上实现的ROM/RAM重映射过程。

; --- Integrator CM control reg

CM_ctl_reg EQU 0x1000000C ;定义CM控制寄存器地址

Remap_bit EQU 0x04 ;CM控制寄存器重映射掩码

ENTRY

;复位异常处理程序开始

; 执行跳转指令,转到实际的ROM执行

LDR pc, =Instruct_2

Instruct_2

; 设置CM控制寄存器的重映射位

LDR r1, =CM_ctl_reg

LDR r0, [r1]

ORR r0, r0, #Remap_bit

STR r0, [r1]

; 重映射后,RAM在0x0地址

; 将向量表从ROM拷贝到 RAM (由 __main函数完成)

13.5.3 局部存储器设置有关的考虑事项

许多ARM处理器内核具有片上存储器系统,如MMU或MPU。这些设备通常是在系统启动过程中进行设置并启用的。因此,带有局部存储器系统的内核的初始化序列需要特别地考虑。

在前面所述的代码启动的过程中,__main中C库初始化代码负责建立代码执行时的内存映像,在跳转到__main前,必须建立处理器内核的运行时存储器视图。这就是说,在复位处理程序中必须设置并启用MMU或MPU。

另外,在跳转到__main前(通常在MMU/MPU设置前),必须启用紧耦合存储器TCM(Tightly coupled Memory),因为在通常情况下都是采用分散加载方法将代码和数据装入TCM。当TCM启用后,用户不必存取由TCM屏蔽的存储器。

在跳转到__main前,如果启用了Cache,可能还会遇到Cache一致性的问题,__main中的函数将程序代码从其加载域拷贝到执行域,在此过程中将指令作为数据进行处理。这样,一些指令可能被放入数据Cache中,在执行这些指令时,由于找不到地址路径而产生错误。为了避免Cache一致性的问题,在C库初始化序列执行完成后再启用Cache。

13.5.4 栈指针初始化

在程序的初始化代码中,用户必须要为处理器用到的各种模式设置堆栈,也就是说,复位处理程序必须为应用程序所使用的任何执行模式的栈指针分配初始值。

下面的例子显示了如何在初始化代码中启用不同模式下的堆栈。

; 启用系统模式堆栈

LDR r2,INT_System_Stack ;将系统堆栈的全局变量放到r2中

STR sp,[r2] ;将系统堆栈指针存储到系统模式下的sp

; 启用系统堆栈限制 (为ARM编译器的堆栈检测做准备)

SUB r1,sp,#SYSTEM_STACK_SIZE ;跳转堆栈指针

BIC r1,r1,#0x03 ;4字节对齐

MOV r10,r1 ;将堆栈的限制放入r10寄存器(AAPCS规则)

LDR r2,INT_System_Limit ;得到堆栈限制全局变量地址

STR r1,[r2] ;将堆栈限制存入全局变量

; 切换到IRQ模式

MRS r0,CPSR ;得到当前的CPSR值

BIC r0,r0,#MODE_MASK ;清除模式位

ORR r1,r0,#IRQ_MODE ;设为IRQ模式

MSR CPSR_cxsf,r1 ;切换到IRQ模式

;启用IRQ模式堆栈

LDR sp,=INT_Irq_SP ;将IRQ模式堆栈指针放入sp_irq

; 切换到FIQ

ORR r1,r0,#FIQ_MODE ;设置FIQ模式位

MSR CPSR_cxsf,r1 ;切换到FIQ模式

; Set-up FIQ stack

LDR sp,=INT_Fiq_SP ;得到FIQ模式指针

; 切换到Abort模式

ORR r1,r0,#ABT_MODE ;设置Abort模式位

MSR CPSR_cxsf,r1 ;切换到ABT模式

; 启用Abort堆栈

LDR sp,=INT_Abort_SP

; 切换到未定义异常模式

ORR r1,r0,#UNDEF_MODE

MSR CPSR_cxsf,r1

;启用未定义指令模式堆栈

LDR sp,=INT_Undefined_SP

; 启用系统/用户堆栈

……

……

为了设置栈指针,进入每种模式(中断禁用)并为栈指针分配适合的值。要利用软件栈检查,也必须在此设置栈限制。

复位处理程序中设置的栈指针和栈限制值由C库初始化代码作为参数自动传递给__user_initial_stackheap()。因此,不允许__user_initial_stackheap()更改这些值。

下面的例子显示了如何实现__user_initial_stackheap(),该段代码可以和上面的堆栈指针设置程序配合使用。

IMPORT heap_base

EXPORT __user_initial_stackheap()

__user_initial_stackheap()

; 程序中指定栈基地址或在描述文件中指定该地址

LDR r0,=heap_base

; r1 contains SB value

MOV pc,lr

13.5.5 硬件初始化

一般情况下,系统初始化代码和主应用程序是分开的。系统初始化要在主应用程序启动前完成。但部分与硬件相关的系统初始化过程,如启用Cache和中断,必须在C库初始化代码执行完成后才能执行。

为了在进入主应用程序之前,完成系统初始化,可以使用$sub和$super函数标识符在进入主程序之前插入一个例程。这一机制可以在不改变源代码的情况下扩展函数的功能。

下面的例子说明了如何使用$sub和$super函数标识。链接程序通过调用$sub$$main()函数取代对main()的调用。所以用户可以在自己编写的$sub$$main()例程中启用Cache或使能中断。

extern void $Super$$main(void);

void $Sub$$main(void)

{

cache_enable(); // 使能caches

int_enable(); // 使能中断

$Super$$main(); // 调用原来的main()函数

}

在$Sub$$main(void)函数中,链接程序通过调用$Super$$main(),使代码跳转到实际的main()函数。

在完成硬件初始化之后,必须考虑主应用程序运行在何种模式。如果应用程序运行在特权模式(Privileged mode),只需在退出复位处理程序前切换到适当的模式;如果应用程序运行在用户模式下,要在完成系统初始化之后,再切换到用户模式。模式的切换工作,一般在$Sub$$main(void)函数中完成。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

Arm CPU正在从根本上推动AI变革,并造福地球。Arm架构是未来AI计算的基石。​

关键字: ARM AI

近日,Arm推出了Arm® Ethos™-U85神经网络处理器(NPU)和Arm Corstone™-320物联网参考设计平台,旨在满足海量的数据处理和大规模计算,加速推进边缘AI的发展进程。

关键字: ARM

随着嵌入式计算设备基础硬件性能的提升,在通信、工业制造、交通运输等领域,嵌入式系统逐渐承担起更加综合化和关键的任务,这也导致嵌入式软件在结构愈加复杂的同时,其安全性问题也越来越受到重视。堆栈是嵌入式软件中的重要存储结构,...

关键字: 嵌入式软件 堆栈

为了赶超云计算市场上的竞争对手,谷歌正试图通过定制的Arm服务器芯片降低云计算服务成本。

关键字: 谷歌 ARM 定制芯片

嵌入式开发作为一个融合了计算机软硬件和系统工程的综合性领域,其成功与否往往取决于三个核心要素的有效整合与协调。这三个要素分别是:硬件平台的选择与设计、软件开发及其优化、以及系统级的设计与集成。深入理解并熟练掌握这三个方面...

关键字: 嵌入式开发 ARM

嵌入式开发是一种专门针对特定硬件平台设计和实现软件系统的工程实践,它涵盖了从需求分析、系统设计、编程实现、调试测试直到产品部署及维护的全过程。本文将深入探讨嵌入式开发的主要阶段,分解其流程并阐述每个步骤的关键要点,以便于...

关键字: 嵌入式开发 嵌入式软件

随着汽车软件数量爆发式的增长,整个行业都需要重新思考汽车产品的开发流程。为此,Arm推出了丰富的硬件IP、新的系统IP,以及全新的汽车计算与计算子系统产品路线图,旨在为各种汽车应用实现性能、功能安全、可扩展等方面的支持。

关键字: ARM 汽车电子

知名移动芯片设计公司ARM最近迈出重要一步,它正式推出汽车芯片设计。ARM推出的芯片设计方案名叫Neoverse,随同芯片一起推出的还有面向汽车制造商、汽车供应商的新系统。

关键字: ARM 汽车芯片 芯片

随着通用人工智能的发展,数据中心的计算需求逐步提高。针对多模态数据、大模型的推理和训练需要更高的算力支持,而随着算力提升与之而来的还需更关注在功耗方面的优化。对于头部云计算和服务厂商而言,针对专门用例提高每瓦性能变得至关...

关键字: ARM 服务器 AI Neoverse CSS

一直以来,riscv架构都是大家的关注焦点之一。因此针对大家的兴趣点所在,小编将为大家带来riscv架构的相关介绍,详细内容请看下文。

关键字: riscv ARM riscv架构
关闭
关闭