You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SAMV71Q20B芯片mii_is_busy函数内联版本失效问题咨询

SAMV71Q20B芯片MII忙状态检测函数内联后失效问题分析

问题背景

我正在为SAMV71Q20B芯片编写硬件寄存器交互代码,核心是mii_is_busy函数——用于读取MII链路的忙状态。发现一个异常现象:给函数添加__attribute__ ((noinline))属性后,汇编与非内联版本一致,以太网栈能正常响应ping;但不添加该属性时,函数被内联后以太网功能直接失效。


版本1:位域访问版(非内联正常,内联失效)

C代码

// Reads current MII link busy status
u32  mii_is_busy(void)
{
    return !GMAC->GMAC_NSR.bit.IDLE;
}

非内联汇编输出

4B03        ldr r3, =0x40050000
6898        ldr r0, [r3, #8]
F3C00080    ubfx r0, r0, #2, #1
F0800001    eor r0, r0, #1
4770        bx lr
BF00        nop
40050000    .word 0x40050000

内联后的两种汇编实现

Inline version 1

4A05        ldr r2, =0x40050000
6891        ldr r1, [r2, #8]
0749        lsls r1, r1, #29
D1FA        bne 0x0042392C

Inline version 2

4A08        ldr r2, =0x40050000
6890        ldr r0, [r2, #8]
0740        lsls r0, r0, #29
D507        bpl 0x0042399C

版本2:寄存器掩码访问版(非内联正常)

C代码

// Reads current MII link busy status
u32 mii_is_busy(void)
{
    return !(GMAC->GMAC_NSR.reg & GMAC_NSR_IDLE);
} 

对应的汇编输出

4B03        ldr r3, =0x40050000
6898        ldr r0, [r3, #8]
F0800004    eor r0, r0, #4
F3C00080    ubfx r0, r0, #2, #1
4770        bx lr
BF00        nop
40050000    .word 0x40050000

寄存器定义(来自CMSIS头文件)

/* -------- GMAC_NSR : (GMAC Offset: 0x08) (R/ 32) Network Status Register -------- */
#if !(defined(__ASSEMBLER__) || defined(__IAR_SYSTEMS_ASM__))
#if COMPONENT_TYPEDEF_STYLE == 'N'
typedef union { 
  struct {
    uint32_t :1;                        /**< bit:      0  Reserved */
    uint32_t MDIO:1;                    /**< bit:      1  MDIO Input Status                        */
    uint32_t IDLE:1;                    /**< bit:      2  PHY Management Logic Idle                */
    uint32_t :4;                        /**< bit:   3..6  Reserved */
    uint32_t RXLPIS:1;                  /**< bit:      7  LPI Indication                           */
    uint32_t :24;                       /**< bit:  8..31  Reserved */
  } bit;                                /**< Structure used for bit  access */
  uint32_t reg;                         /**< Type used for register access */
} GMAC_NSR_Type;
#endif
#endif /* !(defined(__ASSEMBLER__) || defined(__IAR_SYSTEMS_ASM__)) */

#define GMAC_NSR_OFFSET                     (0x08)                                        /**<  (GMAC_NSR) Network Status Register  Offset */

#define GMAC_NSR_MDIO_Pos                   1                                              /**< (GMAC_NSR) MDIO Input Status Position */
#define GMAC_NSR_MDIO_Msk                   (_U_(0x1) << GMAC_NSR_MDIO_Pos)                /**< (GMAC_NSR) MDIO Input Status Mask */
#define GMAC_NSR_MDIO                       GMAC_NSR_MDIO_Msk                              /**< \deprecated Old style mask definition for 1 bit bitfield. Use GMAC_NSR_MDIO_Msk instead */
#define GMAC_NSR_IDLE_Pos                   2                                              /**< (GMAC_NSR) PHY Management Logic Idle Position */
#define GMAC_NSR_IDLE_Msk                   (_U_(0x1) << GMAC_NSR_IDLE_Pos)                /**< (GMAC_NSR) PHY Management Logic Idle Mask */
#define GMAC_NSR_IDLE                       GMAC_NSR_IDLE_Msk                              /**< \deprecated Old style mask definition for 1 bit bitfield. Use GMAC_NSR_IDLE_Msk instead */
#define GMAC_NSR_RXLPIS_Pos                 7                                              /**< (GMAC_NSR) LPI Indication Position */
#define GMAC_NSR_RXLPIS_Msk                 (_U_(0x1) << GMAC_NSR_RXLPIS_Pos)              /**< (GMAC_NSR) LPI Indication Mask */
#define GMAC_NSR_RXLPIS                     GMAC_NSR_RXLPIS_Msk                            /**< \deprecated Old style mask definition for 1 bit bitfield. Use GMAC_NSR_RXLPIS_Msk instead */
#define GMAC_NSR_MASK                       _U_(0x86)                                      /**< \deprecated (GMAC_NSR) Register MASK  (Use GMAC_NSR_Msk instead)  */
#define GMAC_NSR_Msk                        _U_(0x86)                                      /**< (GMAC_NSR) Register Mask  */

问题分析

1. 编译器生成的汇编是否正确?

所有版本的汇编逻辑均正确:

  • 非内联位域版:正确加载GMAC基地址、读取GMAC_NSR寄存器、提取第2位(IDLE位)并取反,完全匹配C代码逻辑。
  • 内联版本1:读取寄存器后将IDLE位左移到最高位,通过bne判断非零值,对应!IDLE的忙状态判断逻辑。
  • 内联版本2:同样左移IDLE位到最高位,通过bpl判断正数(最高位为0),对应空闲状态的跳转逻辑,也符合C代码意图。

综上,编译器生成的汇编代码逻辑完全准确,没有错误。

2. 是硬件特性导致的问题吗?

是的,问题根源在于SAMV71 GMAC模块的硬件特性:

  • GMAC_NSR的IDLE位是PHY管理逻辑的状态位,这类状态位的读取存在隐含时序要求——硬件需要一定周期更新状态,或者寄存器读取操作本身需要与其他操作保持间隔。
  • 函数内联后,代码变得紧凑,编译器可能会重排指令或合并操作,破坏了GMAC模块要求的访问时序:比如读取IDLE位的操作与其他GMAC寄存器操作距离过近,导致硬件来不及更新状态,读取到陈旧值。
  • 非内联函数的调用会产生栈操作和分支跳转,这些额外指令相当于给硬件留出了足够的等待周期,让IDLE状态能正确反映当前链路状态,因此功能正常。

总结

编译器生成的汇编代码逻辑无错误,问题由SAMV71 GMAC模块的硬件时序特性导致:内联后的紧凑代码破坏了IDLE状态位读取所需的时序,导致状态判断错误,进而引发以太网功能失效;添加noinline属性后,函数调用的额外周期满足了硬件时序要求,功能恢复正常。

内容的提问来源于stack exchange,提问作者Zachary Vander Klippe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:16:07