如何为SoC做ARM CPU适配——以裸核使用malloc为例

news2024/11/18 1:32:41

本文任务:为陌生的SoC平台编写基础软件,适配 malloc() 函数

0 如何为SoC做ARM CPU适配

今时不同往日,我们平时开发/娱乐接触到的消费级MCU/MPU基本山都是包含处理核与一系列外设的SoC。如果熟悉裸片开发,一定会在厂家提供的标准库或者SDK包里找到命名类似 startup.s 的汇编文件;如果熟悉 Linux 移植,则不难联想到 arch/arm/kernel 或者 arch/arm/boot 目录下也有类似的文件。

1 Start.s 是什么

startup.s 文件是引导程序的入口点,通常包含引导加载程序需要执行的初始化代码。这些代码负责设置堆栈、加载内核映像到内存中以及跳转到内核入口点。该文件中常见的内容包括:

  • 中断向量和异常处理程序(interrupt handlers):操作系统需要处理各种中断和异常情况,startup.s 文件通常包含中断向量表的定义和异常处理程序的初始化代码。它会设置和配置中断控制器,并将中断向量指向相应的处理函数。

  • 硬件初始化(init):startup.s 文件可能包含对硬件设备的初始化代码,例如启动时钟、初始化串口、启用内存管理单元(MMU)等。这些代码为操作系统提供了基本的硬件支持。

  • 运行环境设置(config):startup.s 文件可能包含设置运行环境所需的代码。例如,它可以设置执行状态,如处理器模式、特权级别等。还可能设置全局变量或数据结构来存储关键的运行时信息。

  • 内核入口点(entry point):最后,startup.s 文件通常将控制权传递给内核的入口函数。这个内核入口点是操作系统初始化的起点,它接收传递的参数和启动信息,并开始执行操作系统的初始化过程。

熟悉意法半导体的 STM32 微控制器的,就知道类似于中断向量表这些最最最最最基本的配置,一般都是放在 startup.s 里面的。宽泛地说,所有围绕 startup.s 文件开展的工作,就是芯片原厂需要做的 SoC 处理器软件适配,具体到ARM Cortex-A,则可以称作是 CPU软件适配。

2 需要考虑哪些问题?

2.1 提供 malloc 函数实现

既然目标在是在裸核上调用 malloc(),那首先得提供一个它的实现吧。

malloc 函数是 C 标准库中的动态内存分配函数。它用于在程序运行时从堆(Heap)中分配指定大小的内存块,并返回该内存块的起始地址。

使用 malloc 函数可以动态地分配内存,避免了在编译时静态地分配内存的限制。这对于需要在运行时根据需要分配或释放内存的情况非常有用。

malloc 函数的原型如下:

void *malloc(size_t size);

参数 size 指定所需内存块的大小,以字节为单位。返回值是一个指向分配的内存块起始地址的指针,类型为 void*。如果分配失败,则返回 NULL。

malloc这种标准函数,处理器厂商都会提供专门优化的实现,比如 ARM 就针对 ARM 处理器提供买了MicroLib库。这是一个精简的 C 标准库,包含常见的标准库函数,例如内存分配和释放(通过 mallocfree)、字符串操作(例如 strcpystrlen)以及输入输出函数(如 printfscanf)。一般在IDE的工程属性设置中勾选包含即可。

在这里插入图片描述

2.2 提供入口点(entry point)

处理器上电以后从预定位置执行ROM程序,而后跳转到RAM上指定位置执行应用程序,虽然C程序的主流程在main函数中描述,但无论是ROM还是RAM,第一条指令都不是main的入口,那么就需要咱提供跳转到main函数的指令,形如:

	.global main_app
	.balign 0x10
_reset:
	B	main_app

解析每条指令的含义如下:

.global main_app: 这是一个指令宏,用于声明 main_app 是一个全局标识符,该标识符可以在其他文件中引用。

.balign 0x10: 这是对齐指令,将下一个标签或指令地址对齐到 16 字节边界。这种对齐可能对于某些处理器或内存访问要求是必要的,例如某些 ARM 处理器可能需要特定的对齐方式以获得最佳性能。

_reset:: 这是一个标签,用于将 _reset 和下一个指令或数据相关联。标签可以用于跳转、调用或在汇编代码中创建有意义的位置。

B main_app: 这是无条件分支指令(Branch),将程序流转移到名为 main_app 的位置。B 指令将控制权直接跳转到指定的地址。

这段汇编代码的意图是:通过 _reset 标签将程序引导到 main_app 代码的入口点。在 _reset 处,执行一个无条件的分支指令 B,将控制权转移到 main_app 标签/符号(涉及编译原理)处的代码。

2.3 初始化堆栈指针

跳转到main函数的最终目的,是调用malloc函数,而arm处理器上执行函数调用势必发生压栈、出栈等堆栈操作,故而需要提前设置好堆栈,于是我们修改 startup.s 为:

	.set stack_top, 0x2E009000
	.global main_app
.balign 0x10
_reset:
	ldr r3, =stack_top
	mov sp, r3
	B	main_app

.set stack_top, 0x2E009000: 这是一个宏定义,表示将标识符 stack_top 的值设置为 0x2E009000。在这里,stack_top 被用来表示栈顶的地址,即堆栈将会从这个地址开始增长。

ldr r3, =stack_top: 这条指令将 stack_top 的值加载到寄存器 r3 中。

mov sp, r3: 这条指令将 r3 中的值移动到堆栈指针寄存器 sp 中。

2.4 整个程序应该放在什么地方运行?

自然是RAM中啦,软件设计人员应该找系统设计人员要一张系统架构图/地址分配表,据之明确片上存储器的位置和存储容量,然后才能确定如何编写链接文件。

啥是链接文件啊?链接文件(Linker file),也称为链接脚本(Linker script)或连接器脚本,是用于指导链接器(Linker)如何将多个目标文件(Object file)组合在一起以生成可执行文件的文本文件。

我们目前的地址分配表是这样的:
在这里插入图片描述
可以看到 System RAM 的起始地址是0x2E000000,存储容量是64KB,这就是我们的程序在运行时所处的位置(不考虑更上层的Cache和寄存器)。大概划分一下用途,前面 0x9000 的空间用来放指令,从 0x200E9000 开始的空间则用作堆(heap),即 mallocfree 函数管理的空间。
在这里插入图片描述
对应的链接脚本可以写成:

LOAD_REGION 0x2E000000 0x10000
{
	P0 +0
    {
       start_up.o
    }
    P1 +0
    {
        *(+ro)
        *(+rw, +zi)
    }
    
    ARM_LIB_STACK	+0 ALIGN 8 EMPTY 0x1000 {}

    ARM_LIB_HEAP 0x2E009000 EMPTY 0x7000 ; Heap region growing up
    { }
}

可视化的效果为:
在这里插入图片描述

2.5 堆栈指针的初始值是怎么确定的?

原则就一句话——只要保证栈不会和堆或者代码段发生交叠(overlap)就行。这里我把栈放在代码段后面(0x200E9000之前),故而初始值设为:

.set stack_top, 0x2E009000

3 怎么验证适配效果?

答案是不外乎仿真、实测、反汇编三板斧。

3.1 实测验证

前两个很明白,写个测试程序看看适配的函数行为是否符合预期就行,例如用下面这个程序测试 mallocfree

/*
 * author water cutter
 * */
#include <stdlib.h>

unsigned int* ptrs[32] = {0};
unsigned int allocByteNum = 1;

void main_app(){
	ptrs[0] = (unsigned int*)malloc(allocByteNum);
	ptrs[1] = (unsigned int*)malloc(allocByteNum);
	ptrs[2] = (unsigned int*)malloc(allocByteNum);
	free(ptrs);
	ptrs[3] = (unsigned int*)malloc(allocByteNum);
	while(1){ asm("nop"); }
}

比较ptrs[2]和ptrs[3]的值,一致则说明malloc和free功能正确(保证所有分配的地址都在堆空间范围内的前提下):

在这里插入图片描述

3.2 通过反汇编检验

.axf 或者 .out 文件反汇编,在反汇编文件中找到对应的实现,分析确认其功能正确即可。

free() 的反汇编:
在这里插入图片描述
malloc() 的反汇编:
在这里插入图片描述

源码

源码仓库:soc_port_method

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/756777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

运营工具篇

既然要做点事&#xff0c;至少要先做到万事俱备只欠东风。 我们也不用把研发产品看得太神圣&#xff0c;比如拿一张纸做出来一个会旋转的纸杯&#xff0c;碰巧有一群人喜欢它并且愿意为它付费&#xff0c;那么剩下要做的是什么&#xff1f;把这个纸杯量产并送到目标用户面前。…

Baklib团队拜访深证通,加强与合作企业沟通

2023年7月11日&#xff0c;Baklib商务经理、运营经理拜访了深圳证券通信有限公司&#xff08;以下简称为&#xff1a;深证通&#xff09;。 2023年7月11日&#xff0c;Baklib商务经理、运营经理拜访了深圳证券通信有限公司&#xff08;以下简称为&#xff1a;深证通&#xff09…

Java程序员需要掌握的前端知识(一)

对于前端知识&#xff0c;需要进一步巩固和加强&#xff0c;进入企业之后&#xff0c;要具备一定的接口调试&#xff0c;参数接收的能力&#xff0c;以及单体页面的开发&#xff0c;这里我学习一下前端知识巩固一下自身的技术栈和水平。本次笔记是跟学黑马的同名课程&#xff0…

2-5 构建聚合工程

父项目 新建 子模块 创建 父项目的pom文件 自动添加了模块引用 1. 聚合工程里可以分为顶级项目&#xff08;顶级工程、父工程&#xff09;与子工程&#xff0c;这两者的关系其实就是父子继承的关系 子工程在maven里称之为模块&#xff08;module&#xff09;&#xff0c;模…

redis和mongodb数据库的基本操作

目录 一、redis基本操作 1、 string类型数据的命令操作&#xff1a; 2、 list类型数据的命令操作&#xff1a; 3、 hash类型数据的命令操作 4、Keys相关的命令操作 二、MongoDB 基本操作作业 1. 查找练习 2.增加、更新、删除作业 &#xff1a; 3.使用之前的grade数据库…

【开发者指南】MyEclipse是如何支持AngularJS的?

MyEclipse在2015 CI 7版本中就引入了对AngularJS的支持&#xff0c;接着往下看&#xff0c;本文将介绍它包含的功能特性。 MyEclipse v2023.1.1离线版下载 1. 支持AngularJS MyEclipse对AngularJS的初始支持在MyEclipse 2015 CI 7中可用&#xff0c;后续版本将提供更多的Ang…

linux进程间通信的本质

因为进程间具有独立性&#xff0c;你们想用进行进程间通信&#xff0c;难度还是比较大的。 进程间通信的本质就是让不同的进程看到同一份资源。 为什么要进行进程间通信——交互数据、控制、通知等目标 进程间通信的技术背景 进程是具有独立性的。虚拟地址空间页表 保证进程运…

Java基础---常用类大全以及各数据结构的方法大全

目录 前言 一、Math类 二.Scanner类 三、String类、StringBuilder和StringBuffer类 &#x1f496;String类 &#x1f496;StringBuilder和StringBuffer 四.Arrays类 五.Random类 六.时间类 七.ArrayList顺序表 八、LinkedList与链表 九.Stack栈和Queue队列 十.Pri…

UE5 DLC

前言 在网上找了很多文档,并没有介绍DLC如何创建,但是对比多篇文档后,可以总结为DLC也是Pak包,本质上还是补丁包,B站上有一篇视频介绍了: [UE4]如何在虚幻4游戏里添加DLC的教程(中英机翻)_哔哩哔哩_bilibili 但是也感觉不对,因为要改Build.cs文件。故研究了一下插件式…

【python】python五月国内社会消费品零售总额数据分析(代码+数据+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、51CTO技术博主 &#x…

ARM微控制器 AM2432BSEFHIALXR、AM2432BSFFHIALV技术参数(32位MCU)

1、AM2432BSEFHIALXR 32位MCU采用293引脚FCCSP封装&#xff0c;工作频率最高可达800MHz。该微控制器专为需要结合处理和实时通信的工业应用而构建&#xff0c;例如远程I/O模块和电机驱动器。 核心处理器&#xff1a;ARM Cortex-M4F&#xff0c;ARM Cortex-R5F 内核规格&#xf…

2.数据结构--空间复杂度

文章目录 一、空间复杂度讲解二、计算下列经典例题的空间复杂度1.冒泡排序的空间复杂度 O(1)2.斐波那契递归的空间复杂度 O(N)3.计算阶乘递归的空间复杂度 O(N) 三、时间复杂度和空间复杂度的对比四、常见的函数的时间复杂度和空间复杂度的总结 一、空间复杂度讲解 1.空间复杂…

复习第五课 C语言-初识数组

目录 【1】初识数组 【2】一维数组 【3】清零函数 【4】字符数组 【5】计算字符串实际长度 练习&#xff1a; 【1】初识数组 1. 概念&#xff1a;具有一定顺序的若干变量的集合 2. 定义格式&#xff1a; 数组名 &#xff1a;代表数组的首地址&#xff0c;地址常量&…

【iOS】ARC内存管理

内存管理 内存管理的思考方式iOS底层内存管理方式1. tagged pointer2. on-pointer iSA--非指针型iSA3. SideTables&#xff0c;RefcountMap&#xff0c;weak_table_t 内存管理有关修饰符__strong修饰符对象的所有者和对象的生命周期__strong对象相互赋值方法参数中使用__strong…

LeetCode 热题 100(一):哈希。49. 字母异位词分组、128. 最长连续序列。

LeetCode100链接&#xff1a;LeetCode 热题 100 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台 一、49. 字母异位词分组 题目要求&#xff1a; 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。…

【ACM】—蓝桥杯大一暑期集训Day2

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前正在学习C/C、Java、算法等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&#xff…

10秒搞定!教你如何轻松压缩jpg格式图片大小!

大家在日常拍照时&#xff0c;都会发现拍摄出来的JPG图片体积比较大&#xff0c;使用和保存时都会比较麻烦。那么该怎样压缩图片大小呢&#xff1f; 首先&#xff0c;我们需要了解什么是JPG压缩。JPG是一种有损压缩格式&#xff0c;通过减少图像中的信息量来使文件大小缩小。使…

Java实现多文件上传及进度条提示-源码下载

1、方案概述 1、案例框架使用的是springmvc5.3.16版本,jackson使用的是2.13.3版本。 2、前端使用的是Layui2.8.11框架,这个框架上手较为容易。 3、使用关键类CommonsMultipartResolver和MultipartHttpServletRequest实现多文件捕获。 2、效果预览 【选择文件】 【上传过…

Android JNI线程的同步 (十三)

🔥 Android Studio 版本 🔥 🔥 了解线程同步的两个变量 🔥 pthread_mutex_t 互斥锁 线程的互斥: 目前存在两个线程 , 线程A和线程B, 只允许只有一个资源对临界资源进程操作 (大概意思就是 : A线程 进入操作临界资源的时候 , 那么 B线程 就要进行等待 . 等到 A线程…

感受C++模版的所带来的魅力,扎实基础,扩展思维

一、泛型编程思想 首先我们来看一下下面这三个函数&#xff0c;如果学习过了 C函数重载 和 C引用 的话&#xff0c;就可以知道下面这三个函数是可以共存的&#xff0c;而且传值会很方便void Swap(int& left, int& right) {int temp left;left right;right temp; } …