04V_RTT

04V 移植 RT-Thread

主要描述过程中遇到的相关问题和相关记录

程序运行到浮点数时系统崩溃

总结

浮点数操作会导致系统崩溃由 MMU 初始化失败引发

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* mmu config */
struct mem_desc platform_mem_desc[] =
{
    {
        0x00000000,
        0x00000000 + 0x00030000,
        0x00000000,
        DEVICE_MEM
    },
    {
        0x30000000,
        0x30B3FFFF,
        0x30000000,
        DEVICE_MEM
    },
    {
        0x80000000,
        0x80000000 + 0x7E000000,
        0x80000000,
        NORMAL_MEM
    },
};

参数解析

platform_mem_desc 数组定义了一系列内存区域的映射规则,供 MMU(内存管理单元)在系统启动时配置页表。数组中的每个结构体(struct mem_desc)通常包含四个关键参数:

序号 参数名(通常约定) 示例值 含义
1 虚拟地址起始 (vaddr_start) 0x80000000 CPU 访问该内存区域时使用的虚拟地址的起始点。
2 虚拟地址结束 (vaddr_end) 0x80000000 + 0x7E000000 CPU 访问该内存区域时使用的虚拟地址的结束点(或紧随其后的地址)。
3 物理地址起始 (paddr_start) 0x80000000 该内存区域在实际硬件上的物理地址的起始点。
4 内存属性 (attr) NORMAL_MEM 定义该内存区域的访问特性和缓存策略。

内存属性总结

属性宏 描述 主要用途 缓存特性
DEVICE_MEM 设备内存 映射外设寄存器、不可缓存的片上 RAM 等。 不可缓存,每次读写直接操作硬件。
NORMAL_MEM 普通内存 映射主存储器(DRAM/SDRAM),存放代码、堆栈、数据等。 可缓存,由缓存机制加速访问。

参数地址说明

根据 04V 空间地址划分, ‘0x00000000 - 0x00030000’ 为 arm 的私有空间 ‘0x30000000 - 0x30B3FFFF’ 系统共享片上外设配置空间 ‘0x80000000 - 0xFFFFFFFF’ 为 DDR 空间

不同的板卡需要根据寄存器地址空间说明来配置

定时器校准

计时不准确的问题

问题描述

系统中通过 rt_tick_get() 获取的时间与实际时间存在偏差,定时不准确。 分析发现,RT-Thread 内核时钟(tick)由 Generic Timer 提供中断驱动,而定时器频率配置错误导致 tick 周期不正确。

问题原因

  • gtimer_set_counter_frequency() 中手动设置的 CNT_FREQUENCY(如 10MHz)与实际硬件计数器频率不一致。
  • gtimer_set_load_value() 参数使用了错误的步进值,导致 tick 中断周期计算错误。

确认硬件计数器频率

操作与分析

使用 gtimer_get_counter_frequency() 读取系统实际的定时器频率:

1
2
timer_step = gtimer_get_counter_frequency();
rt_kprintf("freqency %u MHz\n", timer_step / 1000000);

输出结果为:

1
freqency 150 MHz

说明:

  • 该值来自 ARM 架构的 CNTFRQ 寄存器,表示 PL1 Physical Timer(Generic Timer) 的输入时钟频率。
  • 该定时器为 ARM Cortex-A 系列(如 Cortex-A15)内置硬件定时器,与 CPU 主频相关但 通常经过分频,即定时器频率 ≤ CPU 主频。

修复方法

修复思路:

使 RT-Thread 的 tick 中断周期与硬件计数频率严格匹配。

具体修改:

  1. 不再手动设置 CNTFRQ,保持硬件默认值:

    1
    
    // gtimer_set_counter_frequency(CNT_FREQUENCY);  // 删除此行
    
  2. 基于实际频率计算 tick 周期:

    1
    2
    
    timer_step = gtimer_get_counter_frequency() / RT_TICK_PER_SECOND;
    gtimer_set_load_value(timer_step);
    
  3. 中断服务函数保持一次重装载:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    void rt_hw_timer_isr(int vector, void *parameter)
    {
        rt_hw_interrupt_mask(GENERIC_TIMER_IRQ_NUM);
        arm_gic_clear_pending_irq(GENERIC_TIMER_ID0, GENERIC_TIMER_IRQ_NUM);
        arm_gic_clear_active(GENERIC_TIMER_ID0, GENERIC_TIMER_IRQ_NUM);
    
        rt_tick_increase();                 // 通知RTT系统时间+1tick
        gtimer_set_load_value(timer_step);  // 仅在ISR中重装载计数值
    
        rt_hw_interrupt_umask(GENERIC_TIMER_IRQ_NUM);
    }
    

校准验证结果

验证方法:

同时通过:

  • 硬件计数值:gtimer_get_counter()
  • 内核tick:rt_tick_get()

分别计算时间间隔并对比。

验证结果:

两者计时结果完全一致,表明系统 tick 与硬件定时器周期完全同步,计时准确。

补充说明

项目 说明
PL1 Physical Timer ARM Cortex-A 架构(如 A15)内置的 Generic Timer,寄存器包括 CNTFRQ、CNTP_TVAL、CNTP_CTL 等。
CPU 主频与定时器频率关系 定时器通常由主时钟分频得到,频率可能为主频的 1/2、1/4 等;由 SoC 硬件设计决定。
准确计时的关键 使用 __get_cntfrq() 获取真实频率,并以此计算每 tick 的装载值。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int rt_hw_timer_init(void)
{
    timer_step = gtimer_get_counter_frequency();
    rt_kprintf("freqency %u MHz\n", timer_step/1000000);

    timer_step = timer_step/RT_TICK_PER_SECOND;
    gtimer_set_load_value(timer_step);

    rt_hw_interrupt_install(GENERIC_TIMER_IRQ_NUM, rt_hw_timer_isr, RT_NULL, "tick");
    rt_hw_interrupt_umask(GENERIC_TIMER_IRQ_NUM);

    GenericTimerInterruptEnable(GENERIC_TIMER_ID0);
    GenericTimerStart(GENERIC_TIMER_ID0);

    return 0;
}
INIT_BOARD_EXPORT(rt_hw_timer_init);

最终结论::

通过读取硬件实际计数频率(CNTFRQ)并以此计算每 tick 周期,RT-Thread 系统时钟与 ARM Generic Timer 完全同步,实现了高精度计时。

-O2 优化下系统崩溃

💬 问题描述

  1. 04V 适配 RTT 后,在 debug 模式下运行正常,但切换到 release 模式后,一旦运行浮点数操作,系统崩溃。
  2. debug 和 release 模式就只有 O0 和 O2 的区别,当优化等级为 O1 时,系统也正常。

分析过程

对比了特定 GCC 版本和目标架构下,优化级别 -O1-O2 分别启用了哪些具体的优化选项

  1. 通过分别运行 arm-none-eabi-gcc -O1 -Q --help=optimizersarm-none-eabi-gcc -O2 -Q --help=optimizers 并提供输出文件(O1_opts.txtO2_opts.txt)实现了这一目标。
  2. 根据对比结果,将 仅属于 -O2 的大量优化选项 手动添加到其 rtconfig.py 配置文件的 -O1 编译标志中。
  3. 报告了一个特定的运行时错误:程序在 -O0-O1 下运行正常,但在 -O2 下系统崩溃
  4. 发现通过禁用 -O2 选项列表中的 -fschedule-insns2 优化后,系统能够正确运行。

临时解决方法

在 release 模式下,编译标志中添加 -fno-schedule-insns2,以临时禁用导致崩溃的指令调度优化。

开启 neno-vfpv4 会导致系统崩溃

问题描述与初步诊断

初始现象: 系统启动时,使用 04v 等指令初始化 VFP/NEON 单元的寄存器(CPACR、NSACR、FPEXC)后,打印 VFP/NEON 状态为“✅启用”。但在 RT-Thread 开始 do components initialization 阶段或首次线程切换时,系统进入无限的 backtrace: 打印循环(硬错误/异常)。

关键发现: 将编译器参数从 -mfpu=neon-vfpv4 改为 -mfpu=vfpv4 后,系统可正常运行。

诊断结论: 问题不在于 VFP/NEON 单元的**启用(Enable)**本身,而是出在 NEON 相关的上下文保存与恢复逻辑上。VFPv4 仅使用 D0-D15 寄存器,而 NEON/VFPv4 使用 D0-D31 全套 32 个 D 寄存器。因此,问题集中在 D16-D31 的处理上。

根源分析(基于 RT-Thread 源码)

通过分析 RT-Thread 的汇编上下文切换文件 (context_gcc.S) 和栈初始化文件 (stack.c),定位到两个核心错误:

错误原因一:FPU 栈帧空间预留不足 (堆栈溢出/错位)

  1. 汇编要求 (context_gcc.S): 在 FPU 启用时,上下文切换代码 (rt_hw_context_switch_exit 的恢复逻辑) 会尝试从栈上恢复 66 个 32 位字 (264 字节) 的 FPU 上下文:
    • D0-D31 寄存器:32 个 64 位寄存器 = 64 个 32 位字
    • FPSCR 寄存器:1 个 32 位字
    • FPEXC 寄存器:1 个 32 位字
  2. 原始 stack.c 缺陷: rt_hw_stack_init 函数中 #ifdef RT_USING_FPU 部分,只预留了一个 4 字节的字 (*(--stk) = 0;),远小于实际所需的 264 字节。
  3. 后果: 第一次线程切换时,栈指针被严重错位,导致 CPU 加载错误的 PC/LR 寄存器值,从而跑飞或进入 Hard Fault。

错误原因二:FPU 栈帧压栈顺序与对齐错误

即使修正了空间不足的问题,如果不按照汇编的恢复顺序来初始化栈帧,或者未满足 ARM EABI 的 8 字节对齐要求,系统仍会崩溃。

  1. 压栈顺序错误:
    • context_gcc.S 中的 FPU 寄存器恢复顺序是:FPEXCFPSCRD16-D31D0-D15
    • 因此,rt_hw_stack_init压栈顺序(从高地址到低地址) 必须是反序的:D0-D15D16-D31FPSCRFPEXC
  2. 8 字节对齐错误:
    • 通用寄存器栈帧(17 个字) + FPU 栈帧(66 个字) = 83 个字 (332 字节)。
    • $332$ 字节不是 $8$ 的倍数,导致栈指针 4 字节错位,违反了 NEON/64 位数据操作的对齐要求,最终导致 Data Abort 或跑飞。

最终解决办法

修正 rt_hw_stack_init

解决问题的核心在于修正 stack.c 中的 rt_hw_stack_init 函数,确保正确的空间、正确的顺序和正确的对齐。

C 代码修改 (在 stack.c 中)

rt_hw_stack_init 函数中的 #ifdef RT_USING_FPU 块替换为以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifdef RT_USING_FPU
    /* VFP/NEON Context (共 66 个 32 位字 = 264 字节) */
    
    /* 1. D0-D15 */
    for (i = 0; i <= 15; i++)
    {
        *(--stk) = 0x00000000;
        *(--stk) = 0x00000000;
    }

    /* 2. D16-D31 */
    for (i = 16; i <= 31; i++)
    {
        *(--stk) = 0x00000000;
        *(--stk) = 0x00000000;
    }

    /* 3. FPSCR */
    *(--stk) = 0x00000000;

    /* 4. FPEXC: EN=1 */
    *(--stk) = 0x40000000; 
#endif

配置调整

  1. 编译器参数: 确保使用正确的 NEON 选项:
    • ' -mfloat-abi=hard'
    • ' -mfpu=neon-vfpv4'
  2. 栈大小: 由于 FPU 栈帧增加了 268 字节,强烈建议将所有线程的栈大小(如 RT_MAIN_THREAD_STACK_SIZE)适当增加,例如从 2048 调整到 30724096,以避免线程启动失败(堆分配不足)。

问题

为什么通过寄存器填:0xdeadbeef,而 D0-D15 填0x00000000?

  1. 通用寄存器 (R4-R12, LR, PC 等): 我们通常用 0xdeadbeef 或其他模式值初始化。
  2. 0xdeadbeef 是一个调试辅助标记,它的值本身对程序的正确运行没有影响。
  3. 浮点寄存器(D0-D31)和通用寄存器有着本质的区别:它们保存的是数据,而不是控制流或调试标记。
  4. 浮点寄存器: 用 0x00000000 来保证数据符合 IEEE 754 规范,并避免在新线程启动时立即触发浮点异常。

开启硬件浮点

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

#define NSACR_VFP_ACCESS (3 << 10) // CP10/CP11 bits: [11:10] = 0b11 (Enable Non-secure access)
#define NSACR_NSASEDIS_MASK (3 << 14) // NSASEDIS bit: [15]
#define NSACR_CP10_CP11 (NSACR_VFP_ACCESS) // 0x0C00

void enable_nsacr(void)
{
    unsigned int r0;
    
    // 1. 读取 NSACR: MRC p15, 0, r0, c1, c1, 2
    __asm__ __volatile__(
        "mrc p15, 0, %0, c1, c1, 2" 
        : "=r" (r0) // Output
        : // No Input
        : "memory" // Clobber list
    );
    
    // 2. ORR r0, r0, #(3<<10) ; Enable Non-secure access to CP10 and CP11 (0x0C00)
    // 3. BIC r0, r0, #(3<<14) ; Clear NSASEDIS bit (0xC000)
    r0 = (r0 | NSACR_CP10_CP11) & (~NSACR_NSASEDIS_MASK); 

    // 4. 写入 NSACR: MCR p15, 0, r0, c1, c1, 2
    __asm__ __volatile__(
        "mcr p15, 0, %0, c1, c1, 2"
        : // No Output
        : "r" (r0) // Input
        : "memory"
    );
    
    // 5. ISB (Instruction Synchronization Barrier)
    __asm__ __volatile__("isb" ::: "memory");
}

#define CPACR_FULL_ACCESS (0x3 << 20) // CP10 field: [21:20] = 0b11 (Full Access)
#define CPACR_FULL_ACCESS_EXT (0x3 << 22) // CP11 field: [23:22] = 0b11 (Full Access)
#define CPACR_VFP_NEON (CPACR_FULL_ACCESS | CPACR_FULL_ACCESS_EXT) // 0x00F00000

void enable_cpacr(void)
{
    unsigned int r0 = CPACR_VFP_NEON; // r0 = 0x00F00000

    // 1. 写入 CPACR: MCR p15, 0, r0, c1, c0, 2
    __asm__ __volatile__(
        "mcr p15, 0, %0, c1, c0, 2"
        : // No Output
        : "r" (r0) // Input
        : "memory"
    );

    // 2. ISB
    __asm__ __volatile__("isb" ::: "memory");
}

#define FPEXC_EN_BIT (1 << 30) // EN bit [30] = 1 (Enable)

void enable_fpexc(void)
{
    unsigned int r3 = FPEXC_EN_BIT; // r3 = 0x40000000

    // 1. 写入 FPEXC: VMSR FPEXC, r3
    // 注意:VMSR 是 VFP/NEON 指令,需要特殊处理,通常使用伪指令或编码
    // 在 GCC 内联汇编中,直接使用 VFP 伪指令 `vmsr` 即可。
    __asm__ __volatile__(
        "vmsr fpexc, %0"
        : // No Output
        : "r" (r3) // Input
        : "memory"
    );
}

void arm_vfp_neon_enable(void)
{
    // 1. 配置 NSACR (非安全访问控制)
    enable_nsacr(); 
    
    // 2. 配置 CPACR (协处理器访问控制)
    enable_cpacr(); 
    
    // 3. 配置 FPEXC (全局启用 VFP/NEON)
    enable_fpexc();
}

/**
 * @brief 确认 VFP/NEON 启用状态
 *
 * 通过读取 CPACR, NSACR, 和 FPEXC 寄存器来验证配置是否正确。
 */
void arm_vfp_neon_check_status(void)
{
    unsigned int cpacr_val, nsacr_val, fpexc_val;
    
    // --- 1. 读取 CPACR (c1, c0, 2) ---
    __asm__ __volatile__(
        "mrc p15, 0, %0, c1, c0, 2" 
        : "=r" (cpacr_val)
        :
        : "memory"
    );

    // --- 2. 读取 NSACR (c1, c1, 2) ---
    __asm__ __volatile__(
        "mrc p15, 0, %0, c1, c1, 2" 
        : "=r" (nsacr_val)
        :
        : "memory"
    );

    // --- 3. 读取 FPEXC (VFP/NEON 寄存器) ---
    // 注意:FPEXC 寄存器通过 VMRS 指令访问,需要 VFP 处于某种工作状态。
    __asm__ __volatile__(
        "vmrs %0, fpexc"
        : "=r" (fpexc_val)
        :
        : "memory"
    );

    printf("--- VFP/NEON ---\n");
    printf("CPACR (c1, c0, 2) Value: 0x%08X\n", cpacr_val);
    printf("NSACR (c1, c1, 2) Value: 0x%08X\n", nsacr_val);
    printf("FPEXC Value: 0x%08X\n", fpexc_val);
    printf("------------------------------\n");

    // --- 4. 检查关键位 ---

    // 期望 CPACR 中的 CP10/CP11 (位 [23:20]) 为 0b1111 (0xF)
    // 0x00F00000 检查
    if ((cpacr_val & 0x00F00000) == 0x00F00000) {
        printf("CPACR Status: ✅ CP10/CP11 Full Access Enabled (Secure)\n");
    } else {
        printf("CPACR Status: ❌ CP10/CP11 Access NOT Fully Enabled (Expected 0x00F00000 set)\n");
    }

    // 期望 NSACR 中的 CP10/CP11 (位 [11:10]) 为 0b11 (0xC00)
    // 并且 NSASEDIS (位 [15]) 为 0
    if ((nsacr_val & 0x00000C00) == 0x00000C00 && (nsacr_val & 0x00008000) == 0) {
        printf("NSACR Status: ✅ Non-secure CP10/CP11 Access Enabled\n");
    } else {
        printf("NSACR Status: ❌ Non-secure Access NOT Enabled\n");
    }

    // 期望 FPEXC 中的 EN 位 (位 30) 为 1 (0x40000000)
    if (fpexc_val & 0x40000000) {
        printf("FPEXC Status: ✅ VFP/NEON Global Enable (EN) Set\n");
    } else {
        printf("FPEXC Status: ❌ VFP/NEON Global Enable (EN) NOT Set\n");
    }
}

查看标量/向量浮点指令

1
2
3
4
5
6
7
8
9
# 可以这样查看标量 VFP 指令:
arm-none-eabi-objdump -d rtthread-a15.elf | findstr vadd
arm-none-eabi-objdump -d rtthread-a15.elf | findstr vldr
arm-none-eabi-objdump -d rtthread-a15.elf | findstr vmul
arm-none-eabi-objdump -d rtthread-a15.elf | findstr vstr
arm-none-eabi-objdump -d rtthread-a15.elf | findstr vcmp

arm-none-eabi-objdump -d rtthread-a15.elf |findstr vst1
arm-none-eabi-objdump -d rtthread-a15.elf |findstr vld1

串口有输出无输入

FTDOC4614B_FT-M7004V 片上集成外设使用手册.pdf

2.2.1.7 IRQSn 寄存器 章节介绍:

1
2
3
中断( IRQSn)用来记录 CLC 发送给 ARM 核的可以屏蔽中断,该寄存器可读写,通
过对其写 1 软件清除对应的中断事件标志(标志位清 0),写 0 无效。 224 个外设中断
事件 IRQs 共用 7 个中断标志寄存器( IRQ0~IRQ6)。

2.2.2 寄存器列表 章节介绍:

1
2
3
4
5
6
7
8
CIC 内部有丰富的寄存器, CIC 完成外设中断到 DSP、 ARM 或 DMA 的路由都是通过
对这些内部寄存器进行正确的配置来处理的。 CIC 内部具体的寄存器如表 18 所示:其
寄存器的基地址为 0x30040000。

偏移地址   寄存器名称    描述
...
0x1008    ARM_IRQS0    IRQ0状态寄存器
...

因此,需要写 1 软件清除对应的中断事件标志,具体实现如下所示:

serial/fpl011FPl011InterruptHandler 中断处理函数中

1
2
3
    u32 reg_value2 = *(volatile u32 *)0x30041008;
    volatile u32 *local_addr = (volatile u32 *)0x30041008;
    *local_addr = reg_value2;

清除中断事件标志。

优化后:

1
2
3
4
5
6
7
// parameters.h
#define CIC_IRSQ0_OFFSET                    0x1008

// fpl011_intr.c
#include "parameters.h"
    reg_value = FUART_READREG32(CIC_BASE, CIC_IRSQ0_OFFSET);
    FUART_WRITEREG32(CIC_BASE, CIC_IRSQ0_OFFSET, reg_value);

rt-thread-v5.1.0 修改内容

消除警告

  1. components/net/lwip/port/ethernetif.c : 584

    1
    2
    3
    4
    
        /* set name */
        // rt_strncpy(netif->name, name, NETIF_NAMESIZE);
        rt_strncpy(netif->name, name, NETIF_NAMESIZE - 1);
        netif->name[NETIF_NAMESIZE - 1] = '\0';
    
  2. components/net/netdev/src/netdev.c : 100

    1
    2
    3
    4
    5
    
        /* fill network interface device */
        // rt_strncpy(netdev->name, name, RT_NAME_MAX);
        rt_strncpy(netdev->name, name, RT_NAME_MAX - 1);
        netdev->name[RT_NAME_MAX - 1] = '\0';
        netdev->user_data = user_data;
    
  3. src/object6.c : 384

    1
    2
    3
    4
    5
    6
    7
    
    #if RT_NAME_MAX > 0
        // rt_strncpy(object->name, name, RT_NAME_MAX);  /* copy name */
        rt_strncpy(object->name, name, RT_NAME_MAX - 1);
        object->name[RT_NAME_MAX - 1] = '\0';
    #else
        object->name = name;
    #endif /* RT_NAME_MAX > 0 */
    

bug 修复

libcpu/arm/cortex-a/cpuport.h

1
2
3
4
/* VFP/NEON register count for FPU context */
#ifndef VFP_DATA_NR
#define VFP_DATA_NR 64  /* 32 double-precision registers = 64 words */
#endif

libcpu/arm/cortex-a/stack.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter,
                             rt_uint8_t *stack_addr, void *texit)
{
    rt_uint32_t *stk;
#ifdef RT_USING_FPU
    rt_uint32_t i;
#endif

    stack_addr += sizeof(rt_uint32_t);
    stack_addr  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stack_addr, 8);
    stk      = (rt_uint32_t *)stack_addr;
    *(--stk) = (rt_uint32_t)_thread_start;  /* entry point */
    *(--stk) = (rt_uint32_t)texit;          /* lr */
    *(--stk) = 0xdeadbeef;                  /* r12 */
    *(--stk) = 0xdeadbeef;                  /* r11 */
    *(--stk) = 0xdeadbeef;                  /* r10 */
    *(--stk) = 0xdeadbeef;                  /* r9 */
    *(--stk) = 0xdeadbeef;                  /* r8 */
    *(--stk) = 0xdeadbeef;                  /* r7 */
    *(--stk) = 0xdeadbeef;                  /* r6 */
    *(--stk) = 0xdeadbeef;                  /* r5 */
    *(--stk) = 0xdeadbeef;                  /* r4 */
    *(--stk) = 0xdeadbeef;                  /* r3 */
    *(--stk) = 0xdeadbeef;                  /* r2 */
    *(--stk) = (rt_uint32_t)tentry;         /* r1 : argument 2 for trampoline */
    *(--stk) = (rt_uint32_t)parameter;      /* r0 : argument 1 */
    /* cpsr */
    if ((rt_uint32_t)tentry & 0x01)
        *(--stk) = SVCMODE | 0x20;          /* thumb mode */
    else
        *(--stk) = SVCMODE;                 /* arm mode   */

#ifdef RT_USING_SMART
    *(--stk) = 0;       /* user lr */
    *(--stk) = 0;       /* user sp*/
#endif
#ifdef RT_USING_FPU
    /* FPU context initialization matches context_gcc.S restore order:
     * Stack layout (high to low): FPEXC -> FPSCR -> D16-D31 -> D0-D15
     */
    stk -= VFP_DATA_NR;
    rt_memset(stk, 0, VFP_DATA_NR * sizeof(rt_uint32_t));  /* Initialize D0-D31 (64 words for 32 double regs) */
    *(--stk) = 0;       /* FPSCR: Floating-Point Status and Control Register */
    *(--stk) = 0x40000000;  /* FPEXC: Enable FPU (bit 30 = EN) */
#endif

    return (rt_uint8_t *)stk;
}
Licensed under CC BY-NC-SA 4.0
最后更新于 Dec 16, 2025 02:49 UTC
Xenithya的个人博客
使用 Hugo 构建
主题 StackJimmy 设计