FreeRtos(Arm M7)中断压栈分析

这篇具有很好参考价值的文章主要介绍了FreeRtos(Arm M7)中断压栈分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 目录

1.前言

2.源码分析

2.1 xPortPendSVHandler源码

2.2 pxPortInitialiseStack源码

3. 问题总结


1.前言

         以Arm M7核为例,当CPU响应中断异常时,第一件事就是保存现场,进行压栈。如果当前使用的是任务堆栈,则压入PSP;如果使用的是系统主堆栈,则压入MSP。在压栈的过程中,xPSR,PC,LR,R12以及R3~R0是由硬件自动完成压栈的,具体的入栈情况如图1所示:

 图1 入栈顺序及入栈后堆栈中的内容(参考M3权威指南)

FreeRtos(Arm M7)中断压栈分析,ARM V7,arm开发

        其中,N为入栈开始时SP的值,在入栈后,新栈顶为N-32,这些硬件自动入栈的寄存器是编译器优先使用来保存中间计算结果的。此外,途中的被保存顺序虽然不是严格按照堆栈操作的顺序来执行,但机器会保证最后入栈的结果是正确的,这么做的主要目的是为了优化指令流的执行。比如,先把PC与xPSR保存,就可以更早的启动服务例程指令的预取(PC入栈后可以指向新的指令),且可以在早期更新xPSR中IPSR位段的值。

      此外,如果程序用到了R4~R11,此时会由软件来负责对这些寄存器进行压栈。

      本文主要以freertos的xPortPendSVHandler和pxPortInitialiseStack为切入点,简单分析下M7在异常前后和堆栈相关的具体行为。

2.源码分析

2.1 xPortPendSVHandler源码

void xPortPendSVHandler( void )
{
    /* This is a naked function. */

    __asm volatile
    (
        "	mrs r0, psp							\n"
        "	isb									\n"
        "										\n"
        "	ldr	r3, pxCurrentTCBConst			\n"/* Get the location of the current TCB. */
        "	ldr	r2, [r3]						\n"
        "										\n"
        "	tst r14, #0x10						\n"/* Is the task using the FPU context?  If so, push high vfp registers. */
        "	it eq								\n"
        "	vstmdbeq r0!, {s16-s31}				\n"
        "										\n"
        "	stmdb r0!, {r4-r11, r14}			\n"/* Save the core registers. */
        "	str r0, [r2]						\n"/* Save the new top of stack into the first member of the TCB. */
        "										\n"
        "	stmdb sp!, {r0, r3}					\n"
        "	mov r0, %0 							\n"
        "	cpsid i								\n"/* Errata workaround. */
        "	msr basepri, r0						\n"
        "	dsb									\n"
        "	isb									\n"
        "	cpsie i								\n"/* Errata workaround. */
        "	bl vTaskSwitchContext				\n"
        "	mov r0, #0							\n"
        "	msr basepri, r0						\n"
        "	ldmia sp!, {r0, r3}					\n"
        "										\n"
        "	ldr r1, [r3]						\n"/* The first item in pxCurrentTCB is the task top of stack. */
        "	ldr r0, [r1]						\n"
        "										\n"
        "	ldmia r0!, {r4-r11, r14}			\n"/* Pop the core registers. */
        "										\n"
        "	tst r14, #0x10						\n"/* Is the task using the FPU context?  If so, pop the high vfp registers too. */
        "	it eq								\n"
        "	vldmiaeq r0!, {s16-s31}				\n"
        "										\n"
        "	msr psp, r0							\n"
        "	isb									\n"
        "										\n"
        #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
            #if WORKAROUND_PMU_CM001 == 1
                "			push { r14 }				\n"
                "			pop { pc }					\n"
            #endif
        #endif
        "										\n"
        "	bx r14								\n"
        "										\n"
        "	.align 4							\n"
        "pxCurrentTCBConst: .word pxCurrentTCB	\n"
        ::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
    );
}

         网上已经有很多博文对xPortPendSVHandler进行了详细的解读,主要过程主要包括包括以下几个步骤:

        ①通过pxCurrentTCBConst定位到当前TCB,并从其首地址获取对应的任务栈顶;

        ②对FPU(浮点处理单元)是否使用的相关检测和对应处理器的压栈处理;

        ③对R4~R11,R14寄存器进行压栈处理(压入任务堆栈);

        ④将PSP和pxCurrentTCBConst压入系统堆栈,关中断,指令隔离;

        ⑤调用vTaskSwitchContext切换TCB至当前最高优先级就的就绪任务;

        ⑥从系统栈弹出PSP和pxCurrentTCBConst,并将PSP指向当前TCB获取新任务堆栈的栈顶;

        ⑦恢复任务被阻塞前的寄存器内容,即从PSP弹出R4~R11,R14;

        ⑧跳转到R14,异常返回。

我当时看到这段代码,始终有一个疑惑,既然LR会在响应异常时由硬件自动压栈,那为何会在Save the core registers过程中又对R14进行压栈呢?这个问题后边再分析。

2.2 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. */

    /* Offset added to account for the way the MCU uses the stack on entry/exit
     * of interrupts, and to ensure alignment. */
    pxTopOfStack--;

    *pxTopOfStack = portINITIAL_XPSR;                                    /* xPSR */
    pxTopOfStack--;
    *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS;             /* LR */

    /* Save code space by skipping register initialisation. */
    pxTopOfStack -= 5;                            /* R12, R3, R2 and R1. */
    *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */

    /* A save method is being used that requires each task to maintain its
     * own exec return value. */
    pxTopOfStack--;
    *pxTopOfStack = portINITIAL_EXC_RETURN;

    pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */

    return pxTopOfStack;
}

        这段代码更简单,是在进行任务创建时,进行任务堆栈的初始化,从栈的起始地址按顺序依次在满减栈中对各寄存器的位置进行初始化。其中, PC指向当前任务的主函数,LR则被初始化为portTASK_RETURN_ADDRESS,这其实指向了prvTaskExitError(一个错误处理函数),该函数在任务主函数在错误return的时候,进行相应的异常处理和告警。那么问题来了,既然LR已经被初始化,为何将R0后的位置初始化成了portINITIAL_EXC_RETURN?这个位置和xPortPendSVHandler中最后跳转的R14位置相对应。

3. 问题总结

        根据权威指南的说法,CPU在响应异常序列时,首先会进行压栈、取向量、更新寄存器(选择堆栈指针MSP/PSP,更新堆栈指针SP,LR及PC)这三部曲。取向量指的是指令总线从向量表中找到对应的异常向量,并在对应的服务程序的入口处进行取指,而更新寄存器部分则可以解释我的疑惑。入栈和取向量完成后,需要更新以下寄存器:

        ①SP: 在执行中断服务程序时,将使用系统堆栈,即MSP, 所以xPortPendSVHandler中的PSP指向的是任务栈顶,与当前TCB相对应,而使用的SP则指的是MSP了;

        ②PSR: IPSR位段会被更新为需要响应的异常号;

        ③PC: PC指向服务例程的入口地址;

        ④LR: LR在此时被赋予了新的含义,其值也被更新为EXC_RETURN,用来标识异常返回时要选择的堆栈指针和特权等级(可以参考control寄存器进行理解),更多含义见图2;

图2  EXC_RETURN位段含义

FreeRtos(Arm M7)中断压栈分析,ARM V7,arm开发

 至此,将xPortPendSVHandler和pxPortInitialiseStack对照进行分析,可以发现:

        ①portINITIAL_EXC_RETURN本身就是初始化给异常返回使用的。如果该任务被异常打断,则CPU在进行异常响应时,首先会将LR进行压栈(栈中位置在PC之后,见图1),再将之更新为portINITIAL_EXC_RETURN(栈中位置在R0之后,值为portINITIAL_EXC_RETURN), 即该位置是专为异常返回使用的;

        ②xPortPendSVHandler中,在完成任务切换、并从新的PSP中弹出值到R14,实际上弹出的是为异常返回赋予新含义的EXC_RETURN;在freertos中,portINITIAL_EXC_RETURN等于0xfffffffd,标识着返回Thumb状态,异常返回后进行线程模式并使用PSP;

至此,疑惑终于解开。文章来源地址https://www.toymoban.com/news/detail-825714.html

到了这里,关于FreeRtos(Arm M7)中断压栈分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【ARM Cortex-M 系列 1 -- Cortex-M0, M3, M4, M7, M33 差异】

    请阅读 【ARM Coresight | AMBA BUS| Armv8/v9 | GCC 专栏导读】 下篇文章:ARM Cortex-M 系列 2 – CPU 之 Cortex-M7 介绍 Cortex-M0/M0+ 介绍 Cortex-M0 是 ARM 公司推出的一款微控制器(MCU)核心。这个核心是基于 ARMv6-M 架构设计的, 只支持 56 条指 令的小指令集,大部分指令是 16 位指令, 是 ARM Cor

    2024年02月17日
    浏览(47)
  • 【ARM Cortex-M 系列 1 -- Cortex-M0, M3, M4, M7, M33, M35P 差异】

    请阅读 【ARM Coresight | AMBA BUS| Armv8/v9 | GCC 专栏导读】 下篇文章:ARM Cortex-M 系列 2 – CPU 之 Cortex-M7 介绍 Cortex-M0/M0+ 介绍 Cortex-M0 是 ARM 公司推出的一款微控制器(MCU)核心。这个核心是基于 ARMv6-M 架构设计的, 只支持 56 条指 令的小指令集,大部分指令是 16 位指令, 是 ARM Cor

    2024年02月05日
    浏览(45)
  • ARM裸机 - 中断处理编程实战_arm断电文件内容丢失

    #define exception_vector_table_base 0xD0037400 #define exception_reset (exception_vector_table_base + 0x00) #define exception_undef (exception_vector_table_base + 0x04) #define exception_sotf_int (exception_vector_table_base + 0x08) #define exception_prefetch (exception_vector_table_base + 0x0C) #define exception_data (exception_vector_table_base + 0x10) #de

    2024年04月16日
    浏览(81)
  • 【ARM】-异常与中断

    中断(Interrupt)机制,即处理器在顺序执行程序指令流的过程中突然被别的请求打断而中止执行当前的程序,转而去处理别的事情,待其处理完了别的事情,然后重新回到之前程序中断的点继续执行之前的程序指令流,其要点如下 中断请求、中断源 打断处理器执行程序指令流的

    2024年02月12日
    浏览(40)
  • ARM按键中断控制事件

    main.c

    2024年02月08日
    浏览(33)
  • ARM中断实验

    main.c do_irq.c key_inc.c

    2024年03月26日
    浏览(46)
  • ARM软中断流程详解

    中断流程:①发生中断操作。②程序跳转至相应中断处理函数。③保存现场、处理功能、恢复现场。④程序跳转正常模式继续运行 注:本文将以软中断swi 2指令,在SVC与user两种模式的切换做详细解释。 先来看发生中断时候的特定函数 这里我们先开辟两个空间,存放发生中断后

    2024年02月11日
    浏览(40)
  • ARM 中断实验

    key.h key.c do_irq.c  main.c  

    2024年02月11日
    浏览(41)
  • 【ARM】Day8 中断

    1. 思维导图 2. 实验要求: 实现KEY1/LEY2/KE3三个按键,中断触发打印一句话,并且灯的状态取反 key1 ---- LED3灯状态取反 key2 ---- LED2灯状态取反 key3 ---- LED1灯状态取反 key3.h led.h key3.c led.c do_irq.c main.c 运行结果:

    2024年02月11日
    浏览(44)
  • 易懂的方式讲解ARM中断原理以及中断嵌套方法

    ARM有七种模式,我们这里只讨论SVC、IRQ和FIQ模式。  我们可以假设ARM核心有两根中断引脚(实际上是看不见的),一根叫 irq pin, 一根叫fiq pin。在ARM的cpsr中,有一个I位和一个F位,分别用来禁止IRQ和FIQ。  先不说中断控制器,只说ARM核心。正常情况下,ARM核都只是机械地随着

    2024年01月21日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包