微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 使用MMU进行地址重映射的启动代码结构探讨

使用MMU进行地址重映射的启动代码结构探讨

时间:11-10 来源:互联网 点击:
本文是对《使用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)在嵌

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top