Falha no barramento ao acessar o periférico interno mcu

0

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.

Kev1
fonte
Obrigado Andrew pelo conselho. Vou tentar movê-lo para o site EE.
1
Esse tipo de firmware do carregador de inicialização que reside no chip normalmente quer que você reserve RAM para eles. É esse o caso? Em caso afirmativo, você já fez isso? E também é provável que haja alguma nota de aplicativo sobre isso, que é uma leitura obrigatória. Como sua depuração parece bastante arcaica, por que você está trabalhando com despejos de registro? Basta definir um ponto de interrupção no manipulador de exceções e exibir o rastreio de instruções, para descobrir exatamente qual linha C é a culpada, juntamente com o estado de todos os registros. Você pode precisar de uma cadeia de ferramentas livre de Eclipse e outras doenças.
Lundin
1
Eu já vi problemas como esse antes, que resultaram da configuração incorreta dos registros de tempo do Flash interno do micro, resultando em um overclock do Flash e, ocasionalmente, retornando um código inválido.
brhans 23/01
1
@Lundin - "livre de Eclipse e outras doenças";)
brhans 23/01
1
Sim - é exatamente a essa questão que estou me referindo. Se os estados de espera do Flash não estiverem definidos corretamente, pode parecer que funciona meio que na maioria das vezes, mas ocasionalmente você encontrará o flash alternando um pouco em algum lugar, pois o núcleo lê instruções ou dados e isso se manifestará em alguns sintomas estranhos.
brhans 23/01