我正在为恩智浦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章。