Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread Studio
RT-Thread活动
瑞萨_RA6M4
基于RT-Thread+RA6M4的虚拟仪器开发:第三章程序启动过程分析
发布于 2022-07-06 22:06:50 浏览:1008
订阅该版
[TOC] # 第三章程序启动过程分析 ## 前言 了解程序从上电开始如何运行第一条代码,运行应用程序之前的代码做了什么等等, 对于今后的开发会大有帮助,我们一定要理解底层的细节,知其然知其所以然,才能在遇到问题时能快速解决问题。 代码见 https://gitee.com/qinyunti/ra6m4 ## 分析流程 对于任何一款芯片,都可以按照如下流程方法分析,举一反三,一通百通。 从以下两个方面进行分析: 1. 芯片手册-> 上电-> 启动过程 以上信息要从对应的芯片架构手册我们这里是CORTEX-M33去查找。 2. 工程配置->连接脚本->中断向量->启动代码->底层初始化代码->系统初始化代码->main 以上过程从工程中,借助仿真器分析。 ## 启动过程了解 从手册RA6M4 Group User’s Manual: Hardware第三章可以看到观其boot的描述 首先有一个引脚MD用于配置启动模式,当复位后检测到MD引脚电平为: MD=1 则为Single-chip mode即,直接从on-chip flash开始运行用户代码。 而MD=0时则为SCI / USB boot mode,即,先运行芯片内部的boot代码,可以通过SCI/USB下载用户代码。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/66810e2b7d014bf3f0d95ea3a3dcb41e.png.webp) 如下所示 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/40fcf08a996d9b8373394e268dd7477d.png.webp) 正常我们使用前者运行用户程序,只有在没有仿真器的情况下需要更新程序时才使用后者。 所以这里只介绍前者,后者有时间可以单独介绍。 我们也可以从原理图可以看到,有跳线J8进行MD的电平配置的。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/4cead8e5d9749bdbbbfd60e5819b0504.png.webp) 另外可以通过第4章看下存储的地址空间 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/32e9162812791291247b4fca71d8ba81.png.webp) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/ed7467f209a9132c9e6f1c7bb7dcaff6.png.webp) 本开发板使用的是R7FA6M4AF3CFB,1MB FLASH,256kB RAM。 按照前者配置启动后,即是CORTEX-M33的启动流程了,所以我们需要参考CORTEX-M33的手册。 从https://developer.arm.com/documentation/100235/latest的手册Arm® Cortex®-M33 Devices Generic User Guide的第Chapter 2 The Cortex-M33 Processor的Exception model的Vector table这一节可以看到 复位时根据INITNSVTOR设定默认的中断向量地址,INITNSVTOR是IP核提供的可配参数,芯片实现时定义,一般是0x0000000,对应on chip flash地址。 根据LOCKNSVTOR引脚可决定,中断向量是否可配置到0x00000000 to 0xFFFFFF80,这也是IP提供的可配参数,具体芯片根据实际实现。中断向量的大小必须是2的指数倍字对齐。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/77530786a8bf49e13c7d548122de6c01.png.webp) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/88be9ad1d8f44f25be1729572939b113.png.webp) 我们看到中断向量的最开始4字节就是SP的初始化值,接下来4字节就是Reset后执行的第一条代码的地址,也就是将该4字节内容的值赋值给PC寄存器,相当于跳转到该处执行。 也就是芯片从on chip flash执行时,先将最开始4字节内容加载到SP寄存器(由于栈位于SRAM中所以内容肯定是0x20xxxxxx),然后从接下来4字节读取指令地址加载到PC寄存器进行执行。后面我们会通过仿真器进行验证。 ## 启动过程代码分析 要查找启动代码一般从连接脚本入手 按照如下可以找到使用的链接脚本 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/017c7bc3dcdd465c569dd44e33173589.png.webp) 打开对应的ld文件 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/c7309501c21027e13aff4408405466d2.png.webp) 可以看到先包含了`INCLUDE memory_regions.ld` 该文件定义了存储区域和芯片手册对应 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/53c46e4fc4f834c92f4d9c8ea35e919d.png.webp) 从以下可以看出将.fixed_vectors代码段放在了FLASH_START处即0x00000000处。根据前面描述正是从这个地方开始加载SP和第一条指令。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/2e11949c6e71ef2f8e0b54911545460d.png.webp) 我们搜索`fixed_vectors` ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/7c2c6dcf78cd7627317c5fd663325360.png.webp) 找到如下定义 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/943dde5948ac25d9ef78b9ca758d4879.png.webp) 继续搜索BSP_SECTION_FIXED_VECTORS 找到如下位置 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/c5a9143ee86134cede3cff7390f5d4f7.png.webp) 可以看到 最开始4字节为 (exc_ptr_t) (&g_main_stack[0] + BSP_CFG_STACK_MAIN_BYTES), 即SP初始化为该值其中 #define BSP_CFG_STACK_MAIN_BYTES (0x400) 然后接下来在4字节为Reset_Handler,即加载Reset_Handler的地址到PC执行。 相当于跳转到Reset_Handler执行。 接下就可以继续查看执行过程 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/0c3ac2b72886ddcf0bb7c0ed2da159ba.png.webp) 为了方便跟踪,我们使用仿真器跟踪执行过程 在Reset_Handler处打断点,再进入仿真 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/5e76d0c5886eeda23e31ba224eb1e98e.png.webp) 此时我们可以查看 0x00000000内存处的值 由于是小端模式,所以最开始4字节为0x20001400,接下来4字节为0x00009729 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/94bd37e36e3b983e18825ae1bf5ffcc8.png.webp) 0x00009729处正是Reset_Handler ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/b12a34fa925a905288fb8ca4c72bcd1a.png.webp) 这时看SP寄存器的值,为0x200013f8
![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/77ab7c971b0218048ffe8986fb90c72e.png.webp) 与之前代码中的g_main_stack+1024少了8字节,这是因为 是从c代码中打的断点,实际是打开函数Reset_Handler里面,由于进入函数编译器会自动添加代码保存LR和使用的寄存器,多了指令 push {r7, lr}压栈了两个字,所以SP减少了8字节(满递减栈) ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20220706/2eb35899b5097e2d5f915b5463e650cf.png.webp) 至此我们已经了解芯片复位到执行第一条代码的过程,我们继续 首先调用了SystemInit进行底层初始化 开始先进性FPU使能配置,栈设置 中断向量基地址寄存器VTOR设置 然后是时钟初始化 `bsp_clock_init` 设置栈检测`__set_MSPLIM(BSP_PRV_STACK_LIMIT);` 初始化bss区域(清0) ` memset(&__bss_start__, 0U, ((uint32_t) &__bss_end__ - (uint32_t) &__bss_start__)); ` 初始化data区域,从flash中复制初始化值到ram中 ` memcpy(&__data_start__, &__etext, ((uint32_t) &__data_end__ - (uint32_t) &__data_start__)); ` 调用初始化函数 ``` int32_t count = __init_array_end - __init_array_start; for (int32_t i = 0; i < count; i++) { __init_array_start[i](); } ``` 更新获取时钟 `SystemCoreClockUpdate();` 其他 PMSAR初始化 `bsp_irq_cfg()`; `bsp_init(NULL)`; 弱定义用户代码可以覆盖 然后进入(如果是gcc编译器) `entry()`; 否则直接进入`main`。 继续看`entry()`调用了`rtthread_startup()`; 继续看就是系统初始化了 ``` int rtthread_startup(void) { rt_hw_interrupt_disable(); /* board level initialization * NOTE: please initialize heap inside board initialization. */ rt_hw_board_init(); /* show RT-Thread version */ rt_show_version(); /* timer system initialization */ rt_system_timer_init(); /* scheduler system initialization */ rt_system_scheduler_init(); #ifdef RT_USING_SIGNALS /* signal system initialization */ rt_system_signal_init(); #endif /* RT_USING_SIGNALS */ /* create init_thread */ rt_application_init(); /* timer thread initialization */ rt_system_timer_thread_init(); /* idle thread initialization */ rt_thread_idle_init(); #ifdef RT_USING_SMP rt_hw_spin_lock(&_cpus_lock); #endif /* RT_USING_SMP */ /* start scheduler */ rt_system_scheduler_start(); /* never reach here */ return 0; } ``` 到开始进行调度执行第一个任务,至此函数不再回来。 `rt_application_init`创建了`main_thread_entry`任务 启动调度之后会执行该任务, 调用`rt_components_init`初始化组件 最终花式进入`main`函数 ``` void main_thread_entry(void *parameter) { extern int main(void); #ifdef RT_USING_COMPONENTS_INIT /* RT-Thread components initialization */ rt_components_init(); #endif /* RT_USING_COMPONENTS_INIT */ #ifdef RT_USING_SMP rt_hw_secondary_cpu_up(); #endif /* RT_USING_SMP */ /* invoke system main function */ #ifdef __ARMCC_VERSION { extern int $Super$$main(void); $Super$$main(); /* for ARMCC. */ } #elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__) main(); #endif } ``` ``` #include "hal_data.h" int main(void) { hal_entry(); return 0; } ``` 我们看到`main_thread_entry`并不是一个死循环 `main`会`return 0` `rt_thread_create`调用`rt_hw_stack_init`时 `stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */` 所以最后任务`return` 时会返回`texit`执行 默认传入的是`_thread_exit` 会将该任务状态修改为不再调度,(是否释放动态内存后续再看代码确认)。 ``` static void _thread_exit(void) { struct rt_thread *thread; register rt_base_t level; /* get current thread */ thread = rt_thread_self(); /* disable interrupt */ level = rt_hw_interrupt_disable(); /* remove from schedule */ rt_schedule_remove_thread(thread); /* remove it from timer list */ rt_timer_detach(&thread->thread_timer); /* change stat */ thread->stat = RT_THREAD_CLOSE; /* insert to defunct thread list */ rt_thread_defunct_enqueue(thread); /* enable interrupt */ rt_hw_interrupt_enable(level); /* switch to next task */ rt_schedule(); } ``` ## 总结 通过以上分析,我们了解了芯片从上电到运行到用户代码的整个流程,相信对于任何一个全新的芯片平台也能很快就进行分析了解。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
qinyunti
这家伙很懒,什么也没写!
文章
30
回答
1
被采纳
0
关注TA
发私信
相关文章
1
(苏州站)RT-Thread物联网开发者沙龙【已结束】
2
(成都站)RT-Thread物联网开发者沙龙
3
(深圳站)RT-Thread物联网开发者沙龙
4
(西安站)RT-Thread物联网开发者沙龙
5
成都站2018 RT-Thread开发者沙龙回顾及PPT下载
6
2018 RT-Thread物联网开发者沙龙(北京站)
7
2018 RT-Thread物联网开发者沙龙(南京站)
8
第十三届研电赛RT-Thread企业专项奖发布通知
9
RT-Thread应用作品征集大赛开始啦!
10
你的投票将决定RT-Thread官网应该优先准备的文档是哪些
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
812
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
2
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部