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

位域底层类型宽度是否影响行为?AM335X PRU寄存器异常问题

位域统一类型后AM335X PRU外设异常原因分析

背景与问题

在对接硬件外设时,我用位域定义32位宽的寄存器。最初按照位宽规则混合使用uint8_t/uint16_t/uint32_t定义位域:

// 更多定义在上方
union  {
    volatile uint32_t I2C_REVNB_HI;

    volatile struct {
        uint16_t FUNC   : 12;
        uint8_t RSVD0   : 2;
        uint8_t SCHEME  : 2;
        uint16_t RSVD1  : 16;
    } I2C_REVNB_HI_bits;
}; // 0x04 - 0x08
// 更多定义在下方

规则为:位宽≤8用uint8_t,8<位宽≤16用uint16_t,16<位宽≤32用uint32_t。为简化代码,我将所有位域的底层类型统一为uint32_t:

// 更多定义在上方
union  {
    volatile uint32_t I2C_REVNB_HI;

    volatile struct {
        uint32_t FUNC    : 12;
        uint32_t RSVD0   : 2;
        uint32_t SCHEME  : 2;
        uint32_t RSVD1   : 16;
    } I2C_REVNB_HI_bits;
}; // 0x04 - 0x08
// 更多定义在下方

但统一类型后外设行为异常,需要明确两种实现的底层差异。代码运行在AM335X处理器的可编程实时单元(PRU)上,使用clpru编译器编译。

clpru编译器位域规则

根据clpru编译器文档,位域处理遵循以下规则:

  • 位域按声明的底层类型处理
  • volatile位域按其声明类型访问,多次访问不会被合并
  • 结构体的大小由位域的声明类型决定

测试过程与结果

Update 1:赋值测试结果一致

执行赋值测试后,两种实现的输出均为0x0000CFFF,无差异:

i2c_refactored.I2C_REVNB_HI = 0;
i2c_refactored.I2C_REVNB_HI_bits.SCHEME = ~0;
i2c_refactored.I2C_REVNB_HI_bits.FUNC   = ~0;
DEBUG_MEMORY_0.status = i2c_refactored.I2C_REVNB_HI;

i2c_original.I2C_REVNB_HI = 0;
i2c_original.I2C_REVNB_HI_bits.SCHEME = ~0;
i2c_original.I2C_REVNB_HI_bits.FUNC   = ~0;
DEBUG_MEMORY_1.status = i2c_original.I2C_REVNB;

Update 2:改用unsigned int仍异常

将位域底层类型改为unsigned int后,外设异常问题依然存在。

Update 3:汇编对比未发现逻辑错误但问题持续

编写测试用例并生成汇编代码(DEBUG_MEM0地址为0x10000,DEBUG_MEM1为0x10248),未发现明显逻辑错误,但外设异常依旧。

测试用例代码:

int main(void){

    volatile pru_I2C_tmp i2c_refactored = {0};
    volatile pru_I2C i2c_working = {0};

    i2c_refactored.I2C_REVNB_HI = 0;
    i2c_refactored.I2C_REVNB_HI_bits.SCHEME = ~0;
    i2c_refactored.I2C_REVNB_HI_bits.FUNC   = ~0;
    DEBUG_MEM0.status = i2c_refactored.I2C_REVNB_HI;

    i2c_working.I2C_REVNB_HI = 0;
    i2c_working.I2C_REVNB_HI_bits.SCHEME = ~0;
    i2c_working.I2C_REVNB_HI_bits.FUNC   = ~0;
    DEBUG_MEM1.status = i2c_working.I2C_REVNB_HI;

    __halt();
    return 0;
}

对应汇编代码:

[0x0000] 0x240000c0    LDI R0.w2, 0x0000
[0x0001] 0x24080080    LDI R0.w0, 0x0800
[0x0002] 0x0504e0e2    SUB R2, R0, 0x04
[0x0003] 0x2eff818e    UNKNOWN-F2
[0x0004] 0x230007c3    JAL R3.w2, 0x0007
[0x0005] 0x240001ee    LDI R14, 0x0001
[0x0006] 0x230040c3    JAL R3.w2, 0x0040
[0x0007] 0x05ffe2e2    SUB R2, R2, 0xff
[0x0008] 0x240800ef    LDI R15, 0x0800
[0x0009] 0x2400d8f0    LDI R16, 0x00d8
[0x000a] 0xe1fd02c3    SBBO R3.b2, R2, 253, 2
[0x000b] 0x05b3e2e2    SUB R2, R2, 0xb3
[0x000c] 0x0100e2ee    ADD R14, R2, 0x00
[0x000d] 0x230033c3    JAL R3.w2, 0x0033
[0x000e] 0x2408d8ef    LDI R15, 0x08d8
[0x000f] 0x2400d8f0    LDI R16, 0x00d8
[0x0010] 0x01d8e2ee    ADD R14, R2, 0xd8
[0x0011] 0x230033c3    JAL R3.w2, 0x0033
[0x0012] 0x240000e0    LDI R0, 0x0000
[0x0013] 0x24c000e1    LDI R1, 0xc000
[0x0014] 0xe1042280    SBBO R0, R2, 4, 4
[0x0015] 0x0104e2e0    ADD R0, R2, 0x04
[0x0016] 0xf100208e    LBBO R14, R0, 0, 4
[0x0017] 0x12e1eee1    OR R1, R14, R1
[0x0018] 0xe1002081    SBBO R1, R0, 0, 4
[0x0019] 0x240fffe1    LDI R1, 0x0fff
[0x001a] 0xf100208e    LBBO R14, R0, 0, 4
[0x001b] 0x12e1eee1    OR R1, R14, R1
[0x001c] 0x2eff818e    UNKNOWN-F2
[0x001d] 0xe1002081    SBBO R1, R0, 0, 4
[0x001e] 0x240001c1    LDI R1.w2, 0x0001
[0x001f] 0x24000081    LDI R1.w0, 0x0000
[0x0020] 0xf1042280    LBBO R0, R2, 4, 4
[0x0021] 0xe1002180    SBBO R0, R1, 0, 4
[0x0022] 0x240001c1    LDI R1.w2, 0x0001
[0x0023] 0x24024881    LDI R1.w0, 0x0248
[0x0024] 0xe1dc228e    SBBO R14, R2, 220, 4
[0x0025] 0xf1dd0200    LBBO R0, R2, 221, 1
[0x0026] 0x13c00000    OR R0.b0, R0.b0, 0xc0
[0x0027] 0xe1dd0200    SBBO R0, R2, 221, 1
[0x0028] 0x240fff80    LDI R0.w0, 0x0fff
[0x0029] 0xf1dc02c0    LBBO R0.b2, R2, 220, 2
[0x002a] 0x1280c080    OR R0.w0, R0.w2, R0.w0
[0x002b] 0xe1dc0280    SBBO R0, R2, 220, 2
[0x002c] 0xf1dc2280    LBBO R0, R2, 220, 4
[0x002d] 0xe1002180    SBBO R0, R1, 0, 4
[0x002e] 0x2a000000 >> HALT 
[0x002f] 0x01b3e2e2    ADD R2, R2, 0xb3
[0x0030] 0xf1fd02c3    LBBO R3.b2, R2, 253, 2
[0x0031] 0x01ffe2e2    ADD R2, R2, 0xff
[0x0032] 0x20c30000    JMP R3.w2
[0x0033] 0x5100f00c    QBEQ 12, R16, 0
[0x0034] 0x10eeeef1    AND R17, R14, R14
[0x0035] 0x24003000    LDI R0.b0, 0x0030
[0x0036] 0x70f00002    QBGE 2, R0.b0, R16
[0x0037] 0x10f0f000    AND R0.b0, R16, R16
[0x0038] 0x0400f0f0    SUB R16, R16, R0.b0
[0x0039] 0xff00cf12    LBBO R18, R15, 0, b0
[0x003a] 0xef00d112    SBBO R18, R17, 0, b0
[0x003b] 0x5100f004    QBEQ 4, R16, 0
[0x003c] 0x0000efef    ADD R15, R15, R0.b0
[0x003d] 0x0000f1f1    ADD R17, R17, R0.b0
[0x003e] 0x21003500    JMP 0x0035
[0x003f] 0x20c30000    JMP R3.w2
[0x0040] 0x230042c3    JAL R3.w2, 0x0042
[0x0041] 0x21004100    JMP 0x0041
[0x0042] 0x10000000    AND R0.b0, R0.b0, R0.b0
[0x0043] 0x20c30000    JMP R3.w2

底层差异与异常原因

对比汇编代码可以发现两种实现的核心差异在于寄存器访问粒度:

  1. 混合类型实现(i2c_working):

    • 修改SCHEME位时([0x0025]-[0x0027]),采用单字节LBBO/SBBO操作,仅读写寄存器的对应字节
    • 修改FUNC位时([0x0028]-[0x002b]),采用半字LBBO/SBBO操作,仅读写寄存器的对应半字
  2. 统一uint32_t实现(i2c_refactored):

    • 修改SCHEME和FUNC位时,均采用32位字LBBO/SBBO操作,每次都完整读写整个32位寄存器

对于硬件外设寄存器而言,部分寄存器对访问宽度有严格要求:

  • 某些寄存器要求原子性的窄宽度访问,32位读写可能会意外修改或触发其他未预期的位
  • 部分状态/控制位在被32位读写时会触发硬件行为(如状态位自动清零、外设启动特定操作),而窄宽度访问则不会触发这类行为

虽然赋值测试的最终寄存器值相同,但两种实现产生的总线事务完全不同,硬件对不同宽度的总线事务响应不一致,这就是导致外设行为异常的根本原因。

内容的提问来源于stack exchange,提问作者lrdewaal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 09:00:56