HC32L110(四) HC32L110的startup启动文件和ld连接脚本
目录
- HC32L110(一) HC32L110芯片介绍和Win10下的烧录
- HC32L110(二) HC32L110在Ubuntu下的烧录
- HC32L110(三) HC32L110的GCC工具链和VSCode开发环境
- HC32L110(四) HC32L110的startup启动文件和ld连接脚本
以下介绍项目中的startup和ld文件, 以及HC32L110的启动机制
仓库地址: https://github.com/IOsetting/hc32l110-template
如果转载, 请注明出处.
关于
因为是面向 GCC Arm Embedded 工具链的版本, 所以 startup 代码和 ld 连接描述脚本都依据 GCC Arm 工具链的格式.
Startup文件说明
startup_hc32l110.c 文件位于 Libraries/CMSIS
// 为下面的 uint32_t 等类型引入定义
#include <stdint.h>
// 将 ptr_func_t 定义为函数指针
typedef void (*ptr_func_t)();
// 下面这三个 __data 开头的变量是一组, 用于载入变量预先定义的值. 这些地址在连接阶段, 根据区域的实际情况被赋值
// __data_start 是载入的目标起始地址
extern uint32_t __data_start;
// __data_end 是载入的目标结束地址
extern uint32_t __data_end;
// 载入值的来源
extern uint32_t __data_load;
// __bss 开头的变量, 代表启动时需要清零的变量, __bss_start 和 __bss_end 分别代表了内存的起始和结束地址, 也是连接阶段会赋值
extern uint32_t __bss_start;
extern uint32_t __bss_end;
extern uint32_t __heap_start;
extern uint32_t __stacktop;
// 初始化, 在进入main函数之前需要执行的方法列表
extern ptr_func_t __init_array_start[];
extern ptr_func_t __init_array_end[];
// 引入外部定义的 SystemInit 和 main 方法
extern int main(void);
extern void SystemInit(void);
// 弱函数别名, 在对应的函数未定义时, 会调用别名对应的函数
#define WEAK_ALIAS(x) __attribute__ ((weak, alias(#x)))
// 下面这些都是中断函数
/* Cortex M3 core interrupt handlers */
void Reset_Handler(void);
void NMI_Handler(void) WEAK_ALIAS(Dummy_Handler);
void HardFault_Handler(void) WEAK_ALIAS(Dummy_Handler);
void SVC_Handler(void) WEAK_ALIAS(Dummy_Handler);
void PendSV_Handler(void) WEAK_ALIAS(Dummy_Handler);
void SysTick_Handler(void) WEAK_ALIAS(Dummy_Handler);
// 直接用中断号作为函数名, 具体的对应关系在 ddl.h 中,
// 这些是沿用官方DDL驱动代码, 将来会替换为直接调用实际的中断处理函数
void IRQ000_Handler(void) WEAK_ALIAS(Dummy_Handler);
void IRQ001_Handler(void) WEAK_ALIAS(Dummy_Handler);
void IRQ002_Handler(void) WEAK_ALIAS(Dummy_Handler);
// 中间略
void IRQ031_Handler(void) WEAK_ALIAS(Dummy_Handler);
/* 将 __stacktop 初始地址记录到 __stack_init.
关于 used 的定义: 即是未被使用, 编译后也需要保留
This attribute, attached to a function, means that code must be emitted
for the function even if it appears that the function is not referenced.
This is useful, for example, when the function is referenced only in
inline assembly.
*/
__attribute__((section(".stack"), used)) uint32_t *__stack_init = &__stacktop;
/* Stack top and vector handler table
中断向量表, 这些函数, 和前面的定义需要一致. 这些函数的实际逻辑在 ddl.h 和 ddl.c中定义.
*/
__attribute__ ((section(".vectors"), used)) void *vector_table[] = {
Reset_Handler,
NMI_Handler,
HardFault_Handler,
0,
0,
0,
0,
0,
0,
0,
SVC_Handler,
0,
0,
PendSV_Handler,
SysTick_Handler,
IRQ000_Handler,
IRQ001_Handler,
IRQ002_Handler,
IRQ003_Handler,
// 中间略
IRQ029_Handler,
IRQ030_Handler,
IRQ031_Handler};
/*
最重要的, 重启后的初始化方法, 由ld文件中的 ENTRY(Reset_Handler) 指定
*/
__attribute__((used)) void Reset_Handler(void)
{
uint32_t *src, *dst;
/* 从 Flash 到 RAM 复制变量值 */
src = &__data_load;
dst = &__data_start;
while (dst < &__data_end) *dst++ = *src++;
/* 清空 bss section */
dst = &__bss_start;
while (dst < &__bss_end) *dst++ = 0;
// 这里调用前面声明的 SystemInit
SystemInit();
// 调用初始化函数列表
for (const ptr_func_t *f = __init_array_start; f < __init_array_end; f++)
{
(*f)();
}
// 调用前面声明的main
main();
}
// 默认的中断处理方法
void Dummy_Handler(void)
{
while (1);
}
LD文件说明
以hc32l110x4.ld为例
/* MEMORY 内存块配置, 格式为
MEMORY
{
name [(attr)] : ORIGIN = origin, LENGTH = len
…
}
*/
MEMORY
{
FLASH (rx): ORIGIN = 0x00000000, LENGTH = 16K
RAM (rwx): ORIGIN = 0x20000000, LENGTH = 2K
}
// 运行一个程序时第一个被执行到的指令称为"入口点", 默认是start, 可以使用"ENTRY"连接脚本命令来设置入口点.参数是一个符号名
ENTRY(Reset_Handler)
/* "SECTIONS"命令是链接脚本中最重要的部分, 段命令格式如下, 会包含多个 secname, 区域必须已经在MEMORY中定义
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
{ contents } >region :phdr =fill
...
}
*/
SECTIONS
{
// 当前地址为FLASH区域起始地址
. = ORIGIN(FLASH);
//
.text : {
// KEEP 命令主要作用是防止垃圾收集机制把这几个重要的节排除在外,另外也保证堆栈和向量表在段中的位置处于最顶端
KEEP(*(.stack))
// 对应startup里面的 section(".vectors")
KEEP(*(.vectors))
KEEP(*(.vectors*))
// .text: 所有的编译出来的代码段,都放在这里
KEEP(*(.text))
// 通过 ALIGN 命令, 将当前地址指针调整到4字节对齐
. = ALIGN(4);
*(.text*)
. = ALIGN(4);
// 常量数据的代码段
KEEP(*(.rodata))
*(.rodata*)
// 当前指针, 调节到4字节对齐后的地址
. = ALIGN(4);
} >FLASH // 这个段放在名为FLASH的内存块
// 初始化方法指针队列
.init_array ALIGN(4): {
__init_array_start = .;
KEEP(*(.init_array))
__init_array_end = .;
} >FLASH
__stacktop = ORIGIN(RAM) + LENGTH(RAM);
// LOADADDR(.data) 获取.data段的加载地址(lma),也就是data段在Flash中存放的起始地址
__data_load = LOADADDR(.data);
// 当前地址为 RAM 区域起始地址
. = ORIGIN(RAM);
// 数据部分, 可以看下面对 __data_start 和 __data_end 的赋值方式
.data ALIGN(4) : {
__data_start = .;
*(.data)
*(.data*)
. = ALIGN(4);
__data_end = .;
} >RAM AT >FLASH // 这些变量位于RAM, 值会从FLASH的对应区域载入
/* 可以看下面对 __bss_start 和 __bss_end 的赋值方式
关于 NOLOAD: The `(NOLOAD)' directive will mark a section to not be loaded
at run time. The linker will process the section normally, but will mark
it so that a program loader will not load it into memory
就是会正常连接, 但是运行时不载入内存
*/
.bss ALIGN(4) (NOLOAD) : {
__bss_start = .;
*(.bss)
*(.bss*)
. = ALIGN(4);
__bss_end = .;
*(.noinit)
*(.noinit*)
} >RAM // 这些变量位于RAM
. = ALIGN(4);
__heap_start = .;
}
参考
- GNU linker ld (GNU Binutils) version 2.39 https://sourceware.org/binutils/docs-2.39/ld/index.html
- GCC链接脚本(.ld)文件详解 https://blog.csdn.net/a2529280665/article/details/121576020
HC32L110(四) HC32L110的startup启动文件和ld连接脚本的更多相关文章
- [ARM] Cortex-M Startup.s启动文件相关代码解释
1. 定义一个段名为CSTACK, 这里: NOROOT表示如何定义的段没有被关联,那么同意会被优化掉,如果不想被优化掉就使用ROOT. 后面的括号里数字表示如下: (1):这个段是2的1次方即2字节 ...
- STM32启动文件详细解析(V3.5.0) 以:startup_stm32f10x_hd.s为例
我用的是IAR,这个貌似是MDK的,不过很有用,大家可以看一下 ;* 文件名 : startup_stm32f10x_hd.s ;* 库版本 : V3.5.0 ;* 说明: 此文件为STM32F10x ...
- win10启动文件夹:
win10启动文件夹: C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
- stm32启动文件 startup_stm32f10x_hd.s
;* 文件名 : startup_stm32f10x_hd.s;* 库版本 : V3.5.0;* 说明: 此文件为STM32F10x高密度 ...
- STM32启动过程--启动文件--分析
一.概述 1.说明 每一款芯片的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道.通过了解启动文件,我们可以体会到处理器的架构.指令集.中断向量安排等内容,是非常值得玩味的. ST ...
- STM32启动文件的选择
移植了同事一个程序,然后死活不能用,发现启动文件错了,明天继续调.真把人折腾死了. stm32给的库文件太琐碎了,正如它的芯片型号一样繁多,例如启动文件: 网上查到的各个文件的解释是: startup ...
- Cortex-M3 .s启动文件分析
1. 基本概念(CMSIS): Cortex Micro-controller Software Interface Standard,微控制器软件接口标准. 2. CMSIS标准的文件结构: a) ...
- startup_LPC17XX.s 启动文件分析
工程中startup_LPC17XX.s是M3的启动文件,启动文件由汇编语言写的,它的作用一般是下面这几个: 1)堆和栈的初始化 2)中断向量表定义 3)地址重映射及中断向量表的转移 4)设置系统时钟 ...
- stm32启动文件ld md hd cl vl xl分析及选择
startup_stm32f10x_cl.s互联型的STM32F105xx,STM32F107xxstartup_stm32f10x_hd.s 大容量的STM32F101xx,STM32F102xx, ...
随机推荐
- jdk8对象集合转map集合
package koukay.Controller.Controllers; import koukay.Portal.webservices.Entity.User; import com.fast ...
- 『忘了再学』Shell流程控制 — 38、while循环和until循环介绍
目录 1.while循环 2.until循环 1.while循环 对while循环来讲,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止.和for循环的第二种格式for((初始 ...
- Vue关于echats的使用(浅显易懂)
安装 npm install echarts --save 引入 (全局) main.js import * as echarts from 'echarts'; Vue.prototype.$ech ...
- C++ 炼气期之数组探幽
1. 数组概念 变量是内存中的一个存储块,大小由声明时的数据类型决定. 数组可以认为是变量的集合,在内存中表现为一片连续的存储区域,其特点为: 同类型多个变量的集合. 每一个变量没有自己的名字. 数组 ...
- arcgis创建postgre企业级数据库
什么是企业级地理数据库? 企业级地理数据库(ArcSD Enterprise,sde)是和 arcGIS 套件集成程度最高的地理数据库:创建时需要用到安装 arcGIS Server 时的 [ecp ...
- 你真的会python中的for循环吗
for 循环是 Python 中的通用序列迭代器:它可以单步遍历任何有序序列中的元素.for 语句适用于字符串.列表.元组.其他内置可迭代对象和类创建的新对象. for 通常比 while 循环更容易 ...
- Linux操作系统(6):进程管理和服务管理
进程的基本介绍 1)在 LINUX 中,每个执行的程序(代码)都称为一个进程.每一个进程都分配一个 ID 号. 2)每一个进程,都会对应一个父进程,而这个父进程可以复制多个子进程.例如 www 服务器 ...
- File类获取功能的方法和File类判断功能的方法
File类获取功能的方法-public String getAbsolutePath() :返回此file的绝对路径名字符串 -public String getPath() :将此File转换为路径 ...
- JAVA学习的第一周
这是发表的第一篇博客,关于Java编程的学习体会如下 1.了解Java的产生与发展时机:1995左右出现Java语言,然后Java的最主要的特点是"跨平台".对于跨平台我不太理解, ...
- SpringCloudGateway微服务网关实战与源码分析 - 中
实战 路由过滤器工厂 路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应.路由过滤器的作用域是特定的路由.SpringCloud Gateway包括许多内置的GatewayFilter ...