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
相关产品推荐
相关产品推荐

