Estou com um erro de falha de barramento no meu samg55 µC com base na arquitetura do braço córtex m4. O problema ocorre esporadicamente e não é 100% reproduzível. Deixe-me dar alguns detalhes.
O código do aplicativo grava periodicamente alguns dados na memória flash interna reservada. Para fazer isso, eu uso o EFC (controlador flash incorporado) do µC que é mapeado no endereço 0x400E0A00 no controlador do sistema.
Esta escrita leva em certas condições a uma falha grave. Despejei os registros e obtive o seguinte:
R4_20001368 R5_00000001 R6_00000338 R7_00000020
R8_00000020 R9_2001bcc8 R10_200128cc R11_0043d3a9
R0_402e0a00 R1_5a03380b R2_5a033800 R3_20000067
R12_200129cc LR_0043320b PC_2000006a PSR_010f0000
SHCSR: 0x00000000
CFSR: 0x00008600
HFSR: 0x40000000
DFSR: 0x00000000
MMFAR: 0x402e0a08
BFAR: 0x402e0a08
AFSR: 0x00000000
CFSR indica uma falha de barramento com o sinalizador BFARVALID definido. Observando o registro BFAR, vejo o valor 0x402e0a08, que o endereço base 0x402e0a00 é apenas um pouco diferente do endereço EFC 0x400E0A00.
Eu procurei no código, mas não consegui encontrar o bug. Aqui está o código suspeito:
Código Assembler
004331d4 <efc_perform_command>:
4331d4: f1a1 030e sub.w r3, r1, #14
4331d8: 2b01 cmp r3, #1
4331da: b537 push {r0, r1, r2, r4, r5, lr}
4331dc: d91d bls.n 43321a <efc_perform_command+0x46>
4331de: f3ef 8310 mrs r3, PRIMASK
4331e2: fab3 f383 clz r3, r3
4331e6: 095b lsrs r3, r3, #5
4331e8: 9301 str r3, [sp, #4]
4331ea: b672 cpsid i
4331ec: f3bf 8f5f dmb sy
4331f0: 4c0b ldr r4, [pc, #44] ; (433220 <efc_perform_command+0x4c>)
4331f2: 2300 movs r3, #0
4331f4: 7023 strb r3, [r4, #0]
4331f6: 4b0b ldr r3, [pc, #44] ; (433224 <efc_perform_command+0x50>)
4331f8: 9d01 ldr r5, [sp, #4]
4331fa: ea03 2202 and.w r2, r3, r2, lsl #8
4331fe: f042 42b4 orr.w r2, r2, #1509949440 ; 0x5a000000
433202: b2c9 uxtb r1, r1
433204: 4311 orrs r1, r2
433206: 4b08 ldr r3, [pc, #32] ; (433228 <efc_perform_command+0x54>)
433208: 4798 blx r3
43320a: b125 cbz r5, 433216 <efc_perform_command+0x42>
43320c: 2301 movs r3, #1
43320e: 7023 strb r3, [r4, #0]
433210: f3bf 8f5f dmb sy
433214: b662 cpsie i
433216: b003 add sp, #12
433218: bd30 pop {r4, r5, pc}
43321a: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff
43321e: e7fa b.n 433216 <efc_perform_command+0x42>
433220: 20001368 .word 0x20001368
433224: 00ffff00 .word 0x00ffff00
433228: 20000067 .word 0x20000067
20000066 <efc_perform_fcr>:
20000066: b082 sub sp, #8
20000068: 6041 str r1, [r0, #4]
2000006a: 6883 ldr r3, [r0, #8]
2000006c: 9301 str r3, [sp, #4]
2000006e: 9b01 ldr r3, [sp, #4]
20000070: 07db lsls r3, r3, #31
20000072: d5fa bpl.n 2000006a <efc_perform_fcr+0x4>
20000074: 9801 ldr r0, [sp, #4]
20000076: f000 000e and.w r0, r0, #14
2000007a: b002 add sp, #8
2000007c: 4770 bx lr
Código C
/**
* \brief Perform the given command and wait until its completion (or an error).
*
* \note Unique ID commands are not supported, use efc_perform_read_sequence.
*
* \param p_efc Pointer to an EFC instance.
* \param ul_command Command to perform.
* \param ul_argument Optional command argument.
*
* \note This function will automatically choose to use IAP function.
*
* \return 0 if successful, otherwise returns an error code.
*/
uint32_t efc_perform_command(Efc *p_efc, uint32_t ul_command,
uint32_t ul_argument)
{
uint32_t result;
irqflags_t flags;
/* Unique ID commands are not supported. */
if (ul_command == EFC_FCMD_STUI || ul_command == EFC_FCMD_SPUI) {
return EFC_RC_NOT_SUPPORT;
}
flags = cpu_irq_save();
/* Use RAM Function. */
result = efc_perform_fcr(p_efc,
EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FARG(ul_argument) |
EEFC_FCR_FCMD(ul_command));
cpu_irq_restore(flags);
return result;
}
/**
* \brief Perform command.
*
* \param p_efc Pointer to an EFC instance.
* \param ul_fcr Flash command.
*
* \return The current status.
*/
__no_inline
RAMFUNC
uint32_t efc_perform_fcr(Efc *p_efc, uint32_t ul_fcr)
{
volatile uint32_t ul_status;
p_efc->EEFC_FCR = ul_fcr;
do {
ul_status = p_efc->EEFC_FSR;
} while ((ul_status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY);
return (ul_status & EEFC_ERROR_FLAGS);
}
O código é compilado com arm-none-eabi-gcc (gcc-arm-none-eabi-7-2017-q4-major) com otimização -Os.
Nota: o problema parece ocorrer com mais frequência em baixa temperatura (0 ° C), portanto, não excluo o problema relacionado ao hardware, mesmo que a faixa operacional de µC seja de -40 a 85 ° C ...
Alguma idéia do que poderia dar errado? Obrigado pela ajuda !
PS: Não estou pronto para culpar os raios cósmicos ... ;-)
EDIT: Adicionando alguns detalhes após mais investigação
Primeiro, o sistema é claramente mais estável com a configuração dinâmica do estado de espera, de acordo com a frequência do núcleo. Isto é verdade para temperaturas próximas a 0 ° C e um pouco menos.
Mas torna-se totalmente instável a -15 ° C.
Suspeito que o oscilador interno que estamos usando tenha um desvio significativo devido à baixa temperatura (e / ou instabilidade de tensão). Este oscilador (16MHz) é usado como fonte de núcleo e também para periféricos como timer e usart. Eu tenho duas outras possibilidades para o oscilador de origem: - Oscilador externo rápido a 12 MHz - Oscilador externo lento a 32.768 KHz
O oscilador a 12 MHz não é muito adequado para gerar um bom relógio para o uart com uma taxa de transmissão de 115200. O oscilador lento pode ser usado para generalizar um relógio pll com frequência entre 24 e 48 MHz, que pode ser usado como fonte para periféricos. Eu tentei esta solução e até agora tenho bons resultados em temperaturas muito baixas, mas não tenho certeza de que seja recomendado, especificamente para o uart. Então, minha pergunta é: usar um pll como fonte para criar uma má prática?
A solução de hardware seria substituir o oscilador rápido externo, selecionando a frequência adequada para o uart, mas não é possível no momento.