This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TMS570LC4357:中断上下文切换中 FPU 寄存器保存/恢复的正确顺序

Guru**** 2529560 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1559086/tms570lc4357-correct-order-for-fpu-register-save-restore-in-interrupt-context-switch

器件型号:TMS570LC4357


工具/软件:

我正在处理一个在中断上下文中需要浮点运算的项目、特别是在快速中断请求 (FIQ) 中。 为了确保保持 FPU 寄存器状态、需要在 ISR 开始时保存 FPU 寄存器并在结束时恢复 根据 A/R 芯片的 ARM 架构参考手册第 B4.1.57 节、我需要保存的寄存器为:

  • D0-D15
  • D16-D31(如果已实现)
  • FPSCR
  • FPEXC

此保存/恢复逻辑的原始实现按照上面列出的顺序推入寄存器、并以相反的顺序弹出、如下所示:

    uint32 fpscr_reg;
    uint32 fpexc_reg;

    __asm__(" vpush {d0-d15}");
    __asm__(" vmrs %0, fpscr" : "=r"(fpscr_reg));
    __asm__(" vmrs %0, fpexc" : "=r"(fpexc_reg));

    /* code to handle interrupt... */
    
    __asm__(" vmsr fpexc, %0" ::"r"(fpexc_reg));
    __asm__(" vmsr fpscr, %0" ::"r"(fpscr_reg));
    __asm__(" vpop {d0-d15}");

这似乎效果很好、但最近、当添加一个新功能时、我发现在单行中执行了几次长浮点运算、得到的结果不正确。 当浮点运算被存储在中间变量中的值分解时、这些错误的结果消失了。

经过大量挖掘后、问题似乎是浮点寄存器的存储顺序错误、这会在进入中断上下文之前显示保存的状态。 对于单行中的长计算、计算状态永远不会保存到 RAM 中、而是生活在 FPU 寄存器中。 当中断触发时、寄存器状态变得混乱、进而影响计算结果。

显然、正确的顺序是 首先存储 FPSCR 和 FPEXC、然后存储通用 FP 寄存器。 如下所示:

    uint32 fpscr_reg;
    uint32 fpexc_reg;

    __asm__(" vmrs %0, fpscr" : "=r"(fpscr_reg));
    __asm__(" vmrs %0, fpexc" : "=r"(fpexc_reg));
    __asm__(" vpush {d0-d15}");
    
    /* code to handle interrupt... */
    
    __asm__(" vpop {d0-d15}");
    __asm__(" vmsr fpexc, %0" ::"r"(fpexc_reg));
    __asm__(" vmsr fpscr, %0" ::"r"(fpscr_reg));

这似乎解决了我在单行中进行长浮点计算时遇到的问题。 也就是说、似乎没有太多关于保存/恢复这些步骤的信息。 我希望得到一些确认、确认此新订购是正确的、并且我不会遗漏此过程中的任何其他关键步骤或 TMS570 的习语。

谢谢!

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我最终发现了这里的问题是什么。 关闭局部变量、fpscr_regfpexc_reg 编译为栈指针的相对偏移量。 vpush然后该指令直接推送到堆栈、更新堆栈指针。 这就是产生了对顺序的依赖,因为如果我们把d0-d15寄存器压入声明局部变量fpscr和存储和之间fpexc,栈指针就会移动,状态寄存器就会被放置在堆栈上适用于通用寄存器的位置。 为了解决该问题、我选择使用push/pop指令保存/恢复状态寄存器。 例如:

    __asm__(" vpush {d0-d15}");
    __asm__(" vmrs r1, fpscr");
    __asm__(" push {r1}");
    __asm__(" vmrs r1, fpexc");
    __asm__(" push {r1}");
    
    /* code to handle interrupt... */
    
    __asm__(" pop {r2}");
    __asm__(" vmsr fpexc, r2");
    __asm__(" pop {r2}");
    __asm__(" vmsr fpscr, r2");
    __asm__(" vpop {d0-d15}");


    这统一了所有寄存器之间的存储/恢复方法、确保不依赖于操作顺序。