您的位置 首页 知识

运用MMU进行地址重映射的发动代码结构讨论

本文是对《使用AXD调试MMU地址映射程序手记(二)》一文的补充,首先对编写启动代码所需要了解的基本知识和大多数初学者可能比较模糊的基本…

本文是对《运用AXD调试MMU地址映射程序手记(二)》一文的弥补,首要对编写发动代码所需求了解的根本知识和大多数初学者或许比较含糊的根本概念作了简略介绍,然后对发动代码的结构或说流程做了一些评论。

事实上,关于一个简略的嵌入式运用,编写发动代码并不困难,但假如要在发动代码中运用MMU并完结地址重映射,有一些要害的进程就值得商酌。

本文仅是笔者学习进程中的一点心得,疏忽之处在所难免,旨在抛砖引玉,给初学者一个考虑的方向,更多以及更威望内容请参阅文后所列的参阅材料。

一、映像文件根本组成

映像文件加载时域包含RO和RW段,运转时域则包含RO、RW和ZI三个段。其间RO和RW段的内容在加载时和运转时是相同的,仅仅存储空间或许不同,而ZI段则是运转时由初始化函数创立的。

RO段:Read-Only段,包含源程序中的CODE段,只读数据段(包含变量的初始化值——可所以恣意变量,大局/部分、静态/动态变量的初值;还包含数据常量——这个常量也可所以大局的或部分的。也就是说,编译器既要为变量分配存储空间——变量是可读写的,并不放在RO段,又要为变量的初值分配存储空间,两者是两回事)。

RW段:可读写段,首要指RW-DATA,也或许有RW-CODE。RW-DATA是指现已初始化的大局变量

ZI段:Zero-Initialized段,首要包含未初始化的大局变量,编译器用0值对其进行初始化。该段中的数据由所以变量,因而也是可读写的,但在映像文件加载时,并不为ZI段分配存储空间,虽然在ADS编译器的Memory map文件中以为Total RW Size = (RW Data + ZI Data)。

二、代码,数据和变量在映像文件中的方位

上面简略总结了映像文件各段的组成。从程序的组成看,能够分为变量、数据和代码,其间变量又分为大局/部分的或静态/动态的,它们的存储空间又是怎么分配的呢?

代码:一般是只读的,由编译器分配存储空间并放到映像文件的RO段。

数据:这儿所指的数据都是常量(若可变则为变量),也包含指针常量,那么也归于只读的数据,也由编译器分配存储空间放到映像文件的RO段。

变量:首要依据生计期来分,因为生计期是按在内存中的生计时刻来界说的,而效果域与存储空间分配无关。

1.大局变量和静态变量:包含静态部分变量和大局/静态指针变量在内,由编译器分配存储空间,已初始化的放到RW段,不然放到ZI段;

2.动态变量:首要是指部分变量,包含部分指针变量在内,占用栈空间。

三、发动进程中的仓库初始化释疑

堆与栈:关于ARM,堆是向上成长的,栈是向下成长的。

部分变量占用栈(stack)空间(但其初始化值为数据,占用RO空间);

程序中动态请求的如malloc()和new函数请求的内存空间占用堆(heap)空间。

————×以下评论不运用semihosting机制×————

因而,在转入C运用程序前,有必要要为C程序预备仓库空间。依据具体的方针渠道的存储器资源,要对仓库的初始化函数__user_initial_stackheap( )进行移植,首要是正确设置堆(heap)和栈(stack)的地址。它能够运用C或ARM汇编言语来编写,并至少回来堆基址(保存在R0中),栈基址(保存在R1)可选。因而一个简略的汇编言语编写的__user_initial_stackheap( )函数如下:

EXPORT__user_initial_stackheap

__user_initial_stackheap

LDRR0, =0x20000;heap base

LDRR1, =0x40000;stack base, optional

MOVPC, R14

该函数的C言语完结可见参阅材料[6]P158页。

留意,假如在工程中没有自界说这个函数,那么缺省情况下,编译器/链接器会把|Image$$ZI$$Limit|作为堆(heap)的基址(即把heap和stack区放置在ZI区域的上方,这也被以为是规范的完结[7])。可是,假如运用scatter文件完结涣散加载机制,链接器并不生成符号|Image$$ZI$$Limit|,这时就有必要自己从头完结__user_initial_stackheap( )函数而且设置好堆基址和栈顶,不然链接时会报错。

仓库区还分为单区模型和双区模型,在双区模型中,还有必要设置仓库约束[4,6,7]。

关于重界说__user_initial_stackheap( )函数时几点要留意的当地:一是不要运用超越96字节的stack,二是不要影响到R12(IP,用作进程间调用的暂存寄存器),三是按规矩回来参数值(R0:heap base;R1:stack base;R2:heap limit;R3:stack limit),四是让堆区坚持8字节对齐[6]。

在发动代码中,还要对各个处理器形式的栈指针进行初始化。这个问题很简单与上面谈到的__user_initial_stackheap()函数的效果相混杂。可从以下几点来加以阐明:

(1)在嵌入式运用中,发动代码分为两个部分:一是体系的初始化,包含中止向量表的树立、时钟、存储体系初始化、要害I/O口初始化、各处理器形式下的栈指针初始化等;二是运用程序初始化(或说C库函数初始化),包含RW段的搬移和ZI段的清零、C运用程序仓库区的树立(__user_initial_stackheap()函数初始化仓库指针)等。

从这个意义上说,两者并没有直接关系。

(2)但两者并不是没有联络的。以单区模型的仓库区为例,因为栈是向下成长的,堆是向上成长的,体系形式的栈指针(与用户形式相同,共用一个R13寄存器来描绘)实际上界说了用户形式下单区模型仓库区的上限,而__user_initial_stackheap()函数中指定的heap基址则成为该仓库区的下限。

因而,假如之前现已对体系形式(用户形式)的栈指针进行了初始化,则在重界说__user_initial_stackheap()函数时,就不需求从头界说stack base了。

四、发动代码的内容和初始化次序评论

前面现已指出,发动代码包含体系初始化以及运用程序运转环境的初始化两个部分,完结初始化后,就能够呼叫用户主程序了。参阅材料[1]、[3]和[5]等都对两个部分的内容以及进程列出了十分明晰但又简略明了的进程,这关于初学者来说略微有点笼统。

假如不需求运用MMU进行地址重映射,那么,结合网上能够收集的示例boot代码以及剖析文档,加上自己着手移植和调试,也是比较简单了解的。假如是运用处理器自带的Remap操控寄存器来进行地址重映射,网上也有相关的代码,例如网友twentyone的boot代码【4510 bootloader的完结与剖析(附源代码)】就十分清楚,别的,在《ARM学习陈述》系列文章中也对其有具体的剖析。

关于在发动进程中要运用MMU进行地址重映射的体系初始化次序,在《运用AXD调试MMU地址映射程序手记(二)》一文中给出了一个参阅进程,并做了必定的阐明。经过进一步参阅威望材料,这儿,对体系初始化次序作了小的改善与批改如下:

①制止一切中止→②初始化时钟→③初始化存储器→④初始化各形式下的栈指针→⑤初始化GPIO→⑥复制映像文件到SDRAM→⑦树立地址重映射表→⑧使能MMU→⑨运用程序初始化(RW&ZI区)→⑩使能异常中止→⑾呼叫主程序(dummyOS)。

首要对使能异常中止和运用程序初始化的次序做了调整,即先进行运用程序的初始化,再使能异常中止,可参阅[3]和[10]。

……

———————————————————————————————————————

【参阅材料】

[1]《ARM体系结构与编程》,杜春雷,清华大学出版社,2003年2月

[2]《ARM嵌入式体系开发——软件设计与优化》,沈建华译,2005年5月

[3]《根据ARM的嵌入式体系程序开发关键》,费浙平,2003年8月

[4]《RealView编译东西开发者攻略》,ARM Limited,2003年1月

[5]《ADS Developer Guide》,ARM Limited,2001年11月

[6]《ADS Compilers and Libraries Guide》,ARM Limited,2001年11月

[7]《ADS Linker and Utilities Guide》,ARM Limited,2001年11月

[8]《MAP文件知道开始》,JOHNNY LEE

[9]《堆与栈的差异》,不知道网友

[10]《运用ADS1.2进行嵌入式软件开发》,ARM Limited

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/275706.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部