当前位置: 首页 > news >正文

RP2040关键汇编函数解释

RP2040关键汇编函数解释

  • 1 FreeRTOS源码下载地址
  • 2 RP2040关键汇编函数解释
    • 2.1 xPortPendSVHandler
    • 2.2 vPortStartFirstTask
      • 2.2.1 vPortStartFirstTask关键代码解释
      • 2.2.2 vPortStartFirstTask源码
    • 2.3 pxPortInitialiseStack

1 FreeRTOS源码下载地址

https://www.freertos.org/
在这里插入图片描述

2 RP2040关键汇编函数解释

2.1 xPortPendSVHandler

void xPortPendSVHandler( void )
{/* This is a naked function. */#if ( configNUMBER_OF_CORES == 1 )__asm volatile("   .syntax unified                     \n""   mrs r0, psp                         \n""                                       \n""   ldr r3, pxCurrentTCBConst2          \n" /* Get the location of the current TCB. */"   ldr r2, [r3]                        \n""                                       \n""   subs r0, r0, #32                    \n" /* Make space for the remaining low registers. */"   str r0, [r2]                        \n" /* Save the new top of stack. */"   stmia r0!, {r4-r7}                  \n" /* Store the low registers that are not saved automatically. */"   mov r4, r8                          \n" /* Store the high registers. */"   mov r5, r9                          \n""   mov r6, r10                         \n""   mov r7, r11                         \n""   stmia r0!, {r4-r7}                  \n"#if portUSE_DIVIDER_SAVE_RESTORE"   movs r2, #0xd                   \n" /* Store the divider state. */"   lsls r2, #28                    \n"/* We expect that the divider is ready at this point (which is* necessary to safely save/restore), because:* a) if we have not been interrupted since we entered this method,*    then >8 cycles have clearly passed, so the divider is done* b) if we were interrupted in the interim, then any "safe" - i.e.*    does the right thing in an IRQ - use of the divider should*    have waited for any in-process divide to complete, saved and*    then fully restored the result, thus the result is ready in*    that case too. */"   ldr r4, [r2, #0x60]             \n" /* SIO_DIV_UDIVIDEND_OFFSET */"   ldr r5, [r2, #0x64]             \n" /* SIO_DIV_UDIVISOR_OFFSET */"   ldr r6, [r2, #0x74]             \n" /* SIO_DIV_REMAINDER_OFFSET */"   ldr r7, [r2, #0x70]             \n" /* SIO_DIV_QUOTIENT_OFFSET *//* We actually save the divider state in the 4 words below* our recorded stack pointer, so as not to disrupt the stack* frame expected by debuggers - this is addressed by* portEXTRA_STACK_SIZE */"   subs r0, r0, #48                \n""   stmia r0!, {r4-r7}              \n"#endif /* portUSE_DIVIDER_SAVE_RESTORE */"   push {r3, r14}                      \n""   cpsid i                             \n""   bl vTaskSwitchContext               \n""   cpsie i                             \n""   pop {r2, r3}                        \n" /* lr goes in r3. r2 now holds tcb pointer. */"                                       \n""   ldr r1, [r2]                        \n""   ldr r0, [r1]                        \n" /* The first item in pxCurrentTCB is the task top of stack. */"   adds r0, r0, #16                    \n" /* Move to the high registers. */"   ldmia r0!, {r4-r7}                  \n" /* Pop the high registers. */"   mov r8, r4                          \n""   mov r9, r5                          \n""   mov r10, r6                         \n""   mov r11, r7                         \n""                                       \n""   msr psp, r0                         \n" /* Remember the new top of stack for the task. */"                                       \n"#if portUSE_DIVIDER_SAVE_RESTORE"   movs r2, #0xd                       \n" /* Pop the divider state. */"   lsls r2, #28                        \n""   subs r0, r0, #48                    \n" /* Go back for the divider state */"   ldmia r0!, {r4-r7}                  \n" /* Pop the divider state. *//* Note always restore via SIO_DIV_UDIVI*, because we will overwrite the* results stopping the calculation anyway, however the sign of results* is adjusted by the h/w at read time based on whether the last started* division was signed and the inputs' signs differed */"   str r4, [r2, #0x60]                 \n" /* SIO_DIV_UDIVIDEND_OFFSET */"   str r5, [r2, #0x64]                 \n" /* SIO_DIV_UDIVISOR_OFFSET */"   str r6, [r2, #0x74]                 \n" /* SIO_DIV_REMAINDER_OFFSET */"   str r7, [r2, #0x70]                 \n" /* SIO_DIV_QUOTIENT_OFFSET */#else /* if portUSE_DIVIDER_SAVE_RESTORE */"   subs r0, r0, #32                    \n" /* Go back for the low registers that are not automatically restored. */#endif /* portUSE_DIVIDER_SAVE_RESTORE */"   ldmia r0!, {r4-r7}                  \n"     /* Pop low registers.  */"                                       \n""   bx r3                               \n""   .align 4                            \n""pxCurrentTCBConst2: .word pxCurrentTCB \n");#else /* if ( configNUMBER_OF_CORES == 1 ) */__asm volatile("   .syntax unified                     \n""   mrs r1, psp                         \n""                                       \n""   adr    r0, ulAsmLocals2             \n" /* Get the location of the current TCB for the current core. */"   ldmia r0!, {r2, r3}                 \n"#if portRUNNING_ON_BOTH_CORES"   ldr r0, [r2]                    \n" /* r0 = Core number */"   lsls r0, r0, #2                 \n""   adds r3, r0                     \n" /* r3 = &pxCurrentTCBs[get_core_num()] */#else"                                   \n" /* r3 = &pxCurrentTCBs[0] */#endif /* portRUNNING_ON_BOTH_CORES */"   ldr    r0, [r3]                     \n" /* r0 = pxCurrentTCB */"                                       \n""   subs r1, r1, #32                    \n" /* Make space for the remaining low registers. */"   str r1, [r0]                        \n" /* Save the new top of stack. */"   stmia r1!, {r4-r7}                  \n" /* Store the low registers that are not saved automatically. */"   mov r4, r8                          \n" /* Store the high registers. */"   mov r5, r9                          \n""   mov r6, r10                         \n""   mov r7, r11                         \n""   stmia r1!, {r4-r7}                  \n"#if portUSE_DIVIDER_SAVE_RESTORE/* We expect that the divider is ready at this point (which is* necessary to safely save/restore), because:* a) if we have not been interrupted since we entered this method,*    then >8 cycles have clearly passed, so the divider is done* b) if we were interrupted in the interim, then any "safe" - i.e.*    does the right thing in an IRQ - use of the divider should*    have waited for any in-process divide to complete, saved and*    then fully restored the result, thus the result is ready in*    that case too. */"   ldr r4, [r2, #0x60]             \n" /* SIO_DIV_UDIVIDEND_OFFSET */"   ldr r5, [r2, #0x64]             \n" /* SIO_DIV_UDIVISOR_OFFSET */"   ldr r6, [r2, #0x74]             \n" /* SIO_DIV_REMAINDER_OFFSET */"   ldr r7, [r2, #0x70]             \n" /* SIO_DIV_QUOTIENT_OFFSET *//* We actually save the divider state in the 4 words below* our recorded stack pointer, so as not to disrupt the stack* frame expected by debuggers - this is addressed by* portEXTRA_STACK_SIZE */"   subs r1, r1, #48                \n""   stmia r1!, {r4-r7}              \n"#endif /* portUSE_DIVIDER_SAVE_RESTORE */#if portRUNNING_ON_BOTH_CORES"   ldr r0, [r2]                    \n" /* r0 = Core number */#else"   movs r0, #0                     \n"#endif /* portRUNNING_ON_BOTH_CORES */"   push {r3, r14}                      \n""   cpsid i                             \n""   bl vTaskSwitchContext               \n""   cpsie i                             \n""   pop {r2, r3}                        \n" /* lr goes in r3. r2 now holds tcb pointer. */"                                       \n""   ldr r1, [r2]                        \n""   ldr r0, [r1]                        \n" /* The first item in pxCurrentTCB is the task top of stack. */"   adds r0, r0, #16                    \n" /* Move to the high registers. */"   ldmia r0!, {r4-r7}                  \n" /* Pop the high registers. */"    mov r8, r4                         \n""    mov r9, r5                         \n""    mov r10, r6                        \n""    mov r11, r7                        \n""                                       \n""   msr psp, r0                         \n" /* Remember the new top of stack for the task. */"                                       \n"#if portUSE_DIVIDER_SAVE_RESTORE"   movs r2, #0xd                       \n" /* Pop the divider state. */"   lsls r2, #28                        \n""   subs r0, r0, #48                    \n" /* Go back for the divider state */"   ldmia r0!, {r4-r7}                  \n" /* Pop the divider state. *//* Note always restore via SIO_DIV_UDIVI*, because we will overwrite the* results stopping the calculation anyway, however the sign of results* is adjusted by the h/w at read time based on whether the last started* division was signed and the inputs' signs differed */"   str r4, [r2, #0x60]                \n" /* SIO_DIV_UDIVIDEND_OFFSET */"   str r5, [r2, #0x64]                \n" /* SIO_DIV_UDIVISOR_OFFSET */"   str r6, [r2, #0x74]                \n" /* SIO_DIV_REMAINDER_OFFSET */"   str r7, [r2, #0x70]                \n" /* SIO_DIV_QUOTIENT_OFFSET */#else /* if portUSE_DIVIDER_SAVE_RESTORE */"   subs r0, r0, #32                   \n" /* Go back for the low registers that are not automatically restored. */#endif /* portUSE_DIVIDER_SAVE_RESTORE */"   ldmia r0!, {r4-r7}                 \n"     /* Pop low registers.  */"                                      \n""   bx r3                              \n""                                      \n""   .align 4                           \n""ulAsmLocals2:                         \n""   .word 0xD0000000                   \n" /* SIO */"   .word pxCurrentTCBs                \n");#endif /* if ( configNUMBER_OF_CORES == 1 ) */
}

2.2 vPortStartFirstTask

2.2.1 vPortStartFirstTask关键代码解释

  • 多核心场景,在多核心系统中,获取当前核心正在运行的任务的 TCB 地址。
#if portRUNNING_ON_BOTH_CORES"   adr r1, ulAsmLocals             \n" /* Get the location of the current TCB for the current core. */"   ldmia r1!, {r2, r3}             \n""   ldr r2, [r2]                    \n" /* r2 = Core number */"   lsls r2, #2                     \n""   ldr r3, [r3, r2]                \n" /* r3 = pxCurrentTCBs[get_core_num()] */
  • adr r1, ulAsmLocals:通过相对地址计算,将ulAsmLocals标签的地址加载到r1(ulAsmLocals是下面定义的本地数据区)。
  • ldmia r1!, {r2, r3}:从ulAsmLocals地址开始,连续读取两个字到r2和r3,并自动更新r1(!表示地址自增)。ulAsmLocals中存储的是
    SIO 寄存器地址(0xD0000000)和pxCurrentTCBs数组地址。
  • ldr r2, [r2]:读取 SIO 寄存器(通常用于获取当前核心 ID)的值到r2,即r2 = 当前核心编号(如 0 或 1)。
  • lsls r2, #2:将核心编号左移 2 位(等价于 ×4,因为 TCB 指针是 32 位,占 4 字节),计算数组索引的字节偏移量。
  • ldr r3, [r3, r2]r3初始为pxCurrentTCBs数组的基地址,加上偏移量后,读取当前核心对应的pxCurrentTCB(当前任务控制块指针)到r3。
  • 初始化任务栈指针(PSP)
"    ldr  r0, [r3]                       \n" /* The first item in pxCurrentTCB is the task top of stack. */
"    adds r0, #32                        \n" /* Discard everything up to r0. */
"    msr  psp, r0                        \n" /* This is now the new top of stack to use in the task. */
  • ldr r0, [r3]:r3是当前任务的 TCB 指针,TCB 的第一个成员是任务栈顶指针(pxTopOfStack),读取该值到r0。
  • adds r0, #32:将栈顶指针增加 32 字节。FreeRTOS 任务栈中,栈顶依次保存了r4-r11等寄存器(共 8 个 32 位寄存器,8×4=32 字节),这里跳过这些已保存的寄存器,指向需要手动恢复的寄存器区域。这个和RP2040的pxPortInitialiseStack相关。
  • msr psp, r0:将r0的值写入 PSP(进程栈指针),设置任务使用的栈指针(用户任务运行在 PSP 栈上)。
  • 切换到用户任务栈模式
"    movs r0, #2                         \n" /* Switch to the psp stack. */
"    msr  CONTROL, r0                    \n"
"    isb                                 \n"
  • movs r0, #2:将r0设为 2(二进制10)。

  • msr CONTROL, r0:写入 CONTROL 寄存器(ARM 的控制寄存器)。CONTROL[1] = 1表示任务运行时使用 PSP 栈,而非 MSP 栈。

  • isb:指令同步屏障,确保 CONTROL 寄存器的修改立即生效,后续指令使用新的栈配置。

  • 恢复任务上下文,为执行用户任务代码做准备。

"    pop  {r0-r5}                        \n" /* Pop the registers that are saved automatically. */
"    mov  lr, r5                         \n" /* lr is now in r5. */
"    pop  {r3}                           \n" /* Return address is now in r3. */
"    pop  {r2}                           \n" /* Pop and discard XPSR. */
  • pop {r0-r5}:从 PSP 栈中弹出r0-r5寄存器的值(这些寄存器在任务切换时被自动保存到栈中)。
  • mov lr, r5:将r5的值(栈中保存的lr,链接寄存器)写入实际的lr寄存器。
  • pop {r3}:弹出栈中的返回地址(任务函数的入口地址)到r3。
  • pop {r2}:弹出栈中的 XPSR(程序状态寄存器)值到r2(这里仅弹出但不使用,因为任务启动时 XPSR 已正确初始化)。
  • 启用中断并启动任务
"    cpsie i                             \n" /* The first task has its context and interrupts can be enabled. */
"    bx   r3                             \n" /* Finally, jump to the user defined task code. */
  • cpsie i:清除中断禁止位(Enable Interrupts),允许系统响应中断(任务启动后需要中断支持调度)。
  • bx r3:跳转到r3存储的地址(用户任务函数的入口),正式启动第一个任务。
  • 为多核心场景提供获取核心 ID 和当前 TCB 的必要数据。
#if portRUNNING_ON_BOTH_CORES"                                   \n""     .align 4                      \n""ulAsmLocals:                       \n""    .word 0xD0000000               \n" /* SIO */"    .word pxCurrentTCBs            \n"
#endif /* portRUNNING_ON_BOTH_CORES */

2.2.2 vPortStartFirstTask源码

代码路径:

FreeRTOSv202406.01-LTS\FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel\portable\ThirdParty\GCC\RP2040\port.c

代码源码:

void vPortStartFirstTask( void )
{#if ( configNUMBER_OF_CORES == 1 )__asm volatile ("   .syntax unified             \n""   ldr  r2, pxCurrentTCBConst1 \n" /* Obtain location of pxCurrentTCB. */"   ldr  r3, [r2]               \n""   ldr  r0, [r3]               \n" /* The first item in pxCurrentTCB is the task top of stack. */"   adds r0, #32                \n" /* Discard everything up to r0. */"   msr  psp, r0                \n" /* This is now the new top of stack to use in the task. */"   movs r0, #2                 \n" /* Switch to the psp stack. */"   msr  CONTROL, r0            \n""   isb                         \n""   pop  {r0-r5}                \n" /* Pop the registers that are saved automatically. */"   mov  lr, r5                 \n" /* lr is now in r5. */"   pop  {r3}                   \n" /* Return address is now in r3. */"   pop  {r2}                   \n" /* Pop and discard XPSR. */"   cpsie i                     \n" /* The first task has its context and interrupts can be enabled. */"   bx   r3                     \n" /* Finally, jump to the user defined task code. */"   .align 4                       \n""pxCurrentTCBConst1: .word pxCurrentTCB\n");#else /* if ( configNUMBER_OF_CORES == 1 ) */__asm volatile ("    .syntax unified                    \n"#if configRESET_STACK_POINTER"   ldr  r0, =0xE000ED08            \n" /* Use the NVIC offset register to locate the stack. */"   ldr r0, [r0]                    \n""   ldr r0, [r0]                    \n""   msr msp, r0                     \n" /* Set the msp back to the start of the stack. */#endif /* configRESET_STACK_POINTER */#if portRUNNING_ON_BOTH_CORES"   adr r1, ulAsmLocals             \n" /* Get the location of the current TCB for the current core. */"   ldmia r1!, {r2, r3}             \n""   ldr r2, [r2]                    \n" /* r2 = Core number */"   lsls r2, #2                     \n""   ldr r3, [r3, r2]                \n" /* r3 = pxCurrentTCBs[get_core_num()] */#else"   ldr r3, =pxCurrentTCBs          \n""   ldr r3, [r3]                    \n"  /* r3 = pxCurrentTCBs[0] */#endif /* portRUNNING_ON_BOTH_CORES */"    ldr  r0, [r3]                       \n" /* The first item in pxCurrentTCB is the task top of stack. */"    adds r0, #32                        \n" /* Discard everything up to r0. */"    msr  psp, r0                        \n" /* This is now the new top of stack to use in the task. */"    movs r0, #2                         \n" /* Switch to the psp stack. */"    msr  CONTROL, r0                    \n""    isb                                 \n""    pop  {r0-r5}                        \n" /* Pop the registers that are saved automatically. */"    mov  lr, r5                         \n" /* lr is now in r5. */"    pop  {r3}                           \n" /* Return address is now in r3. */"    pop  {r2}                           \n" /* Pop and discard XPSR. */"    cpsie i                             \n" /* The first task has its context and interrupts can be enabled. */"    bx   r3                             \n" /* Finally, jump to the user defined task code. */#if portRUNNING_ON_BOTH_CORES"                                   \n""     .align 4                      \n""ulAsmLocals:                       \n""    .word 0xD0000000               \n" /* SIO */"    .word pxCurrentTCBs            \n"#endif /* portRUNNING_ON_BOTH_CORES */);#endif /* if ( configNUMBER_OF_CORES == 1 ) */
}

2.3 pxPortInitialiseStack

pxPortInitialiseStackvPortStartFirstTask是相对应的。vPortStartFirstTask的处理流程就是按照pxPortInitialiseStack初始化的栈结构一一对应起来的。

StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters )
{/* Simulate the stack frame as it would be created by a context switch* interrupt. */pxTopOfStack--;                                          /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */*pxTopOfStack = portINITIAL_XPSR;                        /* xPSR */pxTopOfStack--;*pxTopOfStack = ( StackType_t ) pxCode;                  /* PC */pxTopOfStack--;*pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */pxTopOfStack -= 5;                                       /* R12, R3, R2 and R1. */*pxTopOfStack = ( StackType_t ) pvParameters;            /* R0 */pxTopOfStack -= 8;                                       /* R11..R4. */return pxTopOfStack;
}
http://www.dtcms.com/a/295557.html

相关文章:

  • 旧物回收小程序系统开发——开启绿色生活新篇章
  • 基于区块链的商品销售系统(fiscobcos)
  • 本地部署dify1.7.0流程-windows docker
  • [AI 生成] Flink 面试题
  • 企业ERP系统全模块深度解析:从基础管理到智能运营
  • 算法提升之字符串(字典树)
  • 【C++】标准模板库(STL)—— 学习算法的利器
  • 【Qt开发】信号与槽(一)
  • 【MediaTek】AN7563编译wlan_hwifi出现en_npu.c:42:10: fatal error:
  • 上课啦 | 7月27日 Oracle OCP 19C(直播/面授 )
  • docker pull weaviate 国内拉取失败的问题
  • 面试题(技术面+hr面)
  • odoo欧度软件小程序——删除用户
  • 【Lucene】文件概览
  • 【Java学习|黑马笔记|Day21】IO流综合练习,多线程|常用成员方法,守护线程、礼让线程、插入线程
  • 借助 Amazon Redshift 为具有强大抗风险能力的使用案例提供支持
  • AI结对编程:分布式团队的集体记忆外脑
  • Leetcode力扣解题记录--第2题(加法模拟)
  • (进阶向)Python第十四期OpenCv图像预处理方法[2]
  • ModernBERT如何突破BERT局限?情感分析全流程解析
  • 输电线路微气象在线监测装置:保障电网安全的科技屏障
  • Text2SQL智能问答系统开发(一)
  • 成品电池综合测试仪:保障电池品质与安全的核心工具|深圳鑫达能
  • C++抽象类完全指南
  • 三坐标测量仪高效批量检测轴类传动零件
  • 基于深度学习的图像分类:使用EfficientNet实现高效分类
  • 基础NLP | 常用工具
  • DeepSpeed-FastGen:通过 MII 和 DeepSpeed-Inference 实现大语言模型的高吞吐文本生成
  • 机器翻译编程
  • Unity是如何把3D场景显示到屏幕上的——Unity的渲染过程