为什么不对存储器映射外设寄存器进行写入(LPC43xx)



我正在为恩智浦LPC4330(Arm Cortex M0/M4双核)构建一个应用程序。我正在使用 arm-none-eabi-gcc 4.9.3 进行编译。在我的代码中的某一时刻,我正在执行对(32 位)内存位置的写入。紧接着,如果我从该内存位置读回,大约十分之一的结果表明写入没有发生。稍后的后续读取指示相同的情况,因此它不是暂时性情况。中断在全局级别被禁用,并且编译器生成的汇编程序显然正在尝试写入,那么怎么可能不执行写入操作呢?

具体来说,我正在写信给SGPIO外设中的内存映射寄存器SLICE_MUX_CFG0。写入工作时,外围设备正常工作。当回读指示写入未正常工作时,外围设备无法正常工作。因此,正如回读所示,似乎有问题的寄存器设置不正确。

查看.asm(下面列出),写法很清楚。当我事后回读该值时,它显示为零,鉴于下面的列表,这在我看来是不可能的。如果我在写入之前立即执行读取(请参阅下面的 .c 列表),问题就会消失,这可能是一个线索。

所以上面表明,什么?这是否违反了内存总线使用的某些规则?我查看了 GCC 错误列表,看不到与此相关的任何内容。

该函数遵循源和 ASM,并带有一些注释。这里可能会发生什么?为什么以"存储价值"写入显然没有任何效果?

20000f7c <camera_SGPIO_init_sub>:
; disable interrupts globally
20000f7c:   b672        cpsid   i
20000f7e:   2346        movs    r3, #70 ; 0x46
20000f80:   4a16        ldr r2, [pc, #88]   ; (20000fdc <camera_SGPIO_init_sub+0x60>)
20000f82:   6013        str r3, [r2, #0]
20000f84:   4a16        ldr r2, [pc, #88]   ; (20000fe0 <camera_SGPIO_init_sub+0x64>)
20000f86:   6013        str r3, [r2, #0]
20000f88:   4a16        ldr r2, [pc, #88]   ; (20000fe4 <camera_SGPIO_init_sub+0x68>)
20000f8a:   6013        str r3, [r2, #0]
20000f8c:   4a16        ldr r2, [pc, #88]   ; (20000fe8 <camera_SGPIO_init_sub+0x6c>)
20000f8e:   6013        str r3, [r2, #0]
20000f90:   4a16        ldr r2, [pc, #88]   ; (20000fec <camera_SGPIO_init_sub+0x70>)
20000f92:   3301        adds    r3, #1
20000f94:   6013        str r3, [r2, #0]
20000f96:   4a16        ldr r2, [pc, #88]   ; (20000ff0 <camera_SGPIO_init_sub+0x74>)
20000f98:   6013        str r3, [r2, #0]
20000f9a:   4a16        ldr r2, [pc, #88]   ; (20000ff4 <camera_SGPIO_init_sub+0x78>)
20000f9c:   6013        str r3, [r2, #0]
20000f9e:   4a16        ldr r2, [pc, #88]   ; (20000ff8 <camera_SGPIO_init_sub+0x7c>)
20000fa0:   6013        str r3, [r2, #0]
20000fa2:   4a16        ldr r2, [pc, #88]   ; (20000ffc <camera_SGPIO_init_sub+0x80>)
20000fa4:   6013        str r3, [r2, #0]
20000fa6:   2240        movs    r2, #64 ; 0x40
20000fa8:   4b15        ldr r3, [pc, #84]   ; (20001000 <camera_SGPIO_init_sub+0x84>)
20000faa:   601a        str r2, [r3, #0]
20000fac:   2290        movs    r2, #144    ; 0x90
20000fae:   4b15        ldr r3, [pc, #84]   ; (20001004 <camera_SGPIO_init_sub+0x88>)
20000fb0:   0512        lsls    r2, r2, #20
20000fb2:   601a        str r2, [r3, #0]
; load value
20000fb4:   23c6        movs    r3, #198    ; 0xc6
; load destination address
20000fb6:   4a14        ldr r2, [pc, #80]   ; (20001008 <camera_SGPIO_init_sub+0x8c>)
; store value
20000fb8:   6013        str r3, [r2, #0]
; read value back
20000fba:   6810        ldr r0, [r2, #0]
20000fbc:   4a13        ldr r2, [pc, #76]   ; (2000100c <camera_SGPIO_init_sub+0x90>)
20000fbe:   6013        str r3, [r2, #0]
20000fc0:   4a13        ldr r2, [pc, #76]   ; (20001010 <camera_SGPIO_init_sub+0x94>)
20000fc2:   6013        str r3, [r2, #0]
20000fc4:   4a13        ldr r2, [pc, #76]   ; (20001014 <camera_SGPIO_init_sub+0x98>)
20000fc6:   6013        str r3, [r2, #0]
20000fc8:   4a13        ldr r2, [pc, #76]   ; (20001018 <camera_SGPIO_init_sub+0x9c>)
20000fca:   6013        str r3, [r2, #0]
20000fcc:   4a13        ldr r2, [pc, #76]   ; (2000101c <camera_SGPIO_init_sub+0xa0>)
20000fce:   6013        str r3, [r2, #0]
20000fd0:   4a13        ldr r2, [pc, #76]   ; (20001020 <camera_SGPIO_init_sub+0xa4>)
20000fd2:   6013        str r3, [r2, #0]
20000fd4:   4a13        ldr r2, [pc, #76]   ; (20001024 <camera_SGPIO_init_sub+0xa8>)
20000fd6:   6013        str r3, [r2, #0]
; enable interrupts globally
20000fd8:   b662        cpsie   i
20000fda:   4770        bx  lr
20000fdc:   40086480    .word   0x40086480
20000fe0:   40086484    .word   0x40086484
20000fe4:   40086488    .word   0x40086488
20000fe8:   40086494    .word   0x40086494
20000fec:   40086380    .word   0x40086380
20000ff0:   40086384    .word   0x40086384
20000ff4:   40086388    .word   0x40086388
20000ff8:   4008639c    .word   0x4008639c
20000ffc:   40086208    .word   0x40086208
20001000:   40086204    .word   0x40086204
20001004:   40050064    .word   0x40050064
20001008:   40101080    .word   0x40101080
2000100c:   401010a0    .word   0x401010a0
20001010:   40101090    .word   0x40101090
20001014:   401010a4    .word   0x401010a4
20001018:   40101088    .word   0x40101088
2000101c:   401010a8    .word   0x401010a8
20001020:   40101094    .word   0x40101094
20001024:   401010ac    .word   0x401010ac

编译为上述的C代码如下。

volatile uint32_t vol_dummy_for_read;
#define __SFS(addr, value) *((volatile uint32_t*)addr) = value;
#define SGPIO_SLICE_MUX_CFG0 (*((volatile uint32_t*)  ... some address ... ))
uint32_t camera_SGPIO_init_sub()
{
    __asm volatile ("cpsid i" : : : "memory");
    //  configure pins to SGPIO
    __SFS(P9_0, SCU_SFS_INPUT | 6); // D0, SGPIO0
    __SFS(P9_1, SCU_SFS_INPUT | 6);
    __SFS(P9_2, SCU_SFS_INPUT | 6);
    __SFS(P9_5, SCU_SFS_INPUT | 6);
    __SFS(P7_0, SCU_SFS_INPUT | 7);
    __SFS(P7_1, SCU_SFS_INPUT | 7);
    __SFS(P7_2, SCU_SFS_INPUT | 7);
    __SFS(P7_7, SCU_SFS_INPUT | 7); // D7, SGPIO7
    //  SGPIO8
    __SFS(P4_2, SCU_SFS_INPUT | 7); // PCLK, SGPIO8
    //  configure pins to GPIO
    __SFS(P4_1, SCU_SFS_INPUT | 0); // HSYNC, GPIO2[1]
    //  bring SGPIO clock up to full speed (same as PLL1, M4)
    CGU_BASE_PERIPH_CLK = (0 << 1) | (0 << 11) | (9 << 24);
    //  SLICE_MUX_CFG
    uint32_t SLICE_MUX_CFG_VALUE =
          (1 << 1) /* clock on falling edge */
        | (1 << 2) /* clock from external pin */
        | (3 << 6) /* shift 8 bytes per clock */
        ;
////    see note above (this fixes it)
//vol_dummy_for_read = SGPIO_SLICE_MUX_CFG0 ;
    SGPIO_SLICE_MUX_CFG0  = SLICE_MUX_CFG_VALUE; // A
    uint32_t ret = SGPIO_SLICE_MUX_CFG0;
    SGPIO_SLICE_MUX_CFG8  = SLICE_MUX_CFG_VALUE; // I
    SGPIO_SLICE_MUX_CFG4  = SLICE_MUX_CFG_VALUE; // E
    SGPIO_SLICE_MUX_CFG9  = SLICE_MUX_CFG_VALUE; // J
    SGPIO_SLICE_MUX_CFG2  = SLICE_MUX_CFG_VALUE; // C
    SGPIO_SLICE_MUX_CFG10 = SLICE_MUX_CFG_VALUE; // K
    SGPIO_SLICE_MUX_CFG5  = SLICE_MUX_CFG_VALUE; // F
    SGPIO_SLICE_MUX_CFG11 = SLICE_MUX_CFG_VALUE; // L
    __asm volatile ("cpsie i" : : : "memory");
    return ret;
}

(我正在回答我自己的问题;这个答案是根据上面评论中提供的线索得出的)。

简答题

设未可靠地操作寄存器更新,因为驱动它的外设时钟 (CGU_BASE_PERIPH_CLK) 在写入操作时刚刚更改了其速度。在更新时钟速度时设置 AUTOBLOCK 位可消除该问题。

讨论

据推测,在频率变化期间,外设的时钟暂时无效,具体取决于条件。也许,如果边沿的时序恰好恰好如此,那么非常短的时钟脉冲会在变化过程中找到通过的方式。或者类似不愉快的东西沿着时钟线到达外围设备。在任何情况下,在这些不可预知的情况下,可能不会发生写入,从而导致报告的故障。

可以理解的是,在时钟速度变化和后续分配之间等待一段时间也可以消除这个问题。如问题中所述,在写入之前读取寄存器也可以消除问题;这是否是因为需要时间,还是读取操作块(原因尚不清楚)直到外设时钟稳定,尚不清楚。

AUTOBLOCK仅记录在功能声明中:"在频率变化期间自动阻止时钟"。用户手册没有说明在时钟速度变化期间应设置位或保持清晰的条件。但是,鉴于此处报告的证据,在更新其中一个设备中的时钟速度时始终设置AUTOBLOCK的策略似乎是明智的,除非有已知的原因将其清除。

参考:恩智浦LPC43xx用户手册,UM10503修订版1.9,第13章。

最新更新