RT-Thread 版本:4.1.0 master 版本。
完整工程代码如下:可运行在 Cortex-A53 架构上的 RTT
Arch64 状态下,RT-thead 的任务切换触发未知错误。
经过测试,是因为 idle 初始化时,栈溢出,修改了就绪列表的值,导致任务切换失败.
#ifndef IDLE_THREAD_STACK_SIZE
#if defined (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP)
#define IDLE_THREAD_STACK_SIZE 256
#else
#define IDLE_THREAD_STACK_SIZE 128
#endif /* (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) */
#endif /* IDLE_THREAD_STACK_SIZE */
可以手动设置 IDLE_THREAD_STACK_SIZE 大小更改 IDLE 任务栈大小。问题解决。
串口报错如下:
[0]switch to priority#255 thread:tidle0(sp:0x41019024), from thread:tshell(sp: 0x42058f20)
[SYNC Error]: in EL1
ELR_EL1 =0x000000004100045c
ESR_EL1 =0x000000009a000000
current Exception Level exception, SPsel = 0
x0 =0x0000000000000000 x1 =0x0000000000000001
x2 =0x0000000000000000 x3 =0x0000000000000000
x29 =0x000000000000001d x30 =0x0000000041003134
[INFO]: CPU Reboot now!!!
查看手册得知,ELR_EL1 =0x000000004100045c
,该异常由 sp 指针对齐失败导致的。
解决:由于当前 sp 指针为 4 字节对齐的,所以失败,在 rt_hw_stack_init()
中 将 sp 指针进行 16 字节对齐,问题解决。
/*
* TODO sp 指针4字节对齐不行,64位任务环境下的任务切换会失败,更改为 16 字节对齐
*/
stack_addr = RT_ALIGN_DOWN((rt_ubase_t)stack_addr, sizeof(rt_ubase_t) * 2);
注意:栈指针16字节对齐,是编程向导手册中要求的(8.2.1章节)!!!经过测试,8字节对齐确实不行
测试发现,shell 输入回车以及任一shell命令导致系统 tick 计数清零。
经过调试分析,在rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER)
代码前后将 rt_tick
清零。
继续测试,是rt_sem_release()
中的rt_schedule()
将rt_tick
清零。
继续测试,问题出现在rt_schedule()
中的 rt_hw_context_switch_interrupt()
的汇编实现中。
原因:汇编代码中变量访问越界造成该问题。
在 rt_hw_context_switch_interrupt()
的汇编实现中,会用到三个外部定义的变量:
.globl rt_thread_switch_interrupt_flag
.globl rt_interrupt_from_thread
.globl rt_interrupt_to_thread
这三个外部变量定义在 libcpu 文件夹下对应架构的 interrupt.c 文件中。
volatile rt_ubase_t rt_interrupt_from_thread = 0;
volatile rt_ubase_t rt_interrupt_to_thread = 0;
volatile rt_uint32_t rt_thread_switch_interrupt_flag = 0;
其中,变量 rt_thread_switch_interrupt_flag
定义为无符号的32位整型,而在rt_hw_context_switch_interrupt()
的汇编实现中,我们使用 Xn 寄存器来访问这个变量,而 Xn 寄存器是64位的,导致访问越界,而刚好, rt_thread_switch_interrupt_flag
变量地址为 0x41019144, 变量rt_tick
地址为 0x41019148, 所以当我们以 64 位宽度对 rt_thread_switch_interrupt_flag
置1或清0时,我们都会将 rt_tick
清0。
解决:
变量 rt_thread_switch_interrupt_flag
的初始类型为:
volatile rt_uint32_t rt_thread_switch_interrupt_flag = 0;
现在更改为:
volatile rt_ubase_t rt_thread_switch_interrupt_flag = 0;
问题解决。