Cambiar la latencia flash de 0 a 1, incluso con la captación previa habilitada, ralentiza la ejecución en STM32

He puesto toda la configuración del reloj en su estado predeterminado, por lo que un oscilador interno funciona a 8 mhz. Tengo un ciclo de retardo usando el ensamblaje en línea de la siguiente manera:

// Delay a certain number of cycles using glorious inline assembly.
void delay(uint32_t time) {
    asm volatile(
        "mov r4, #3                \n" // Divide time by three since the loop is
        "udiv %[time], %[time], r4 \n" // 3x too slow.
        "loop:                     \n"
        "subs %[time], %[time], #1 \n" // 1 cycle
        "bne loop                  \n" // 1 cycle if not take, 2 if taken.
        : [time] "+l" (time)           // Put rw input variable time in r0..r7.
                                       // Make it rw and as output so we don't clobber
        :                              // Time is both input and output.
        : "r4", "cc"                   // We are clobbering r4 and condition code flags.
    );
}

y una rutina GPIO de la siguiente manera:

// Blink the led with a period of 1 second.
while (1) {
    // Set LED pin.
    GPIOA_BSRR = 1 << LED_PIN;
    delay(second_cycles / 2);

    // Reset LED pin
    GPIOA_BSRR = 1 << (LED_PIN + 16);
    delay(second_cycles / 2);
}

Cuando se ejecuta sin estados de espera, todo está bien y funciona como se esperaba. Pero cuando cambio los estados de espera para flash de 0 a 1, mi bucle tarda 833 milisegundos en lugar de 500 milisegundos, o una pérdida de rendimiento del 66% aproximadamente.

Cuando uso GDB para depurar, puedo ver que el registro FLASH_ACR tiene un contenido que 0b0011 0000significa que el búfer de captación previa está habilitado y tiene un estado habilitado, con 0 estados de espera para flash. Además, el búfer de captación previa debe habilitarse en el reinicio según la hoja de datos. Cuando escribo en el registro al hacer or'ing, 0b001obtengo el resultado esperado de 0b0011 0001volver después de leerlo nuevamente. Esto se hace haciendo lo siguiente:

// Change the flash wait states to 1.
volatile uint32_t foo1 = FLASH_ACR;
FLASH_ACR = FLASH_ACR | (0b001 << 0);
volatile uint32_t foo2 = FLASH_ACR;

Curiosamente, habilitar o deshabilitar el búfer de búsqueda previa con un estado de espera 1 no hace ninguna diferencia en mi bucle, lo que no parece tener sentido.

Y aquí está la sección relevante de la hoja de datos.Flash ACR

Estoy usando una placa NUCLEO-F303RE , que usa un STM32F103RE.

La hoja de datos no es muy útil. Supongo que el reloj del bus flash se retrasa 1 Tcy para FLASH_ACR = 0x01 y se retrasa 2 para 0x02, independientemente de la captación previa. ¿Probó 0x02 para ver si se degrada otro 66%?
Pensé que guardé los datos relacionados con cuando cambié el estado de espera a 2 tuercas, parece que no lo hice. Por lo que recuerdo, fue de 1,2 a 1,5 segundos. Volveré a comprobar cuando llegue a casa.

Respuestas (1)

El búfer de caché en STM32F303RE tiene solo 8 bytes (64 bits), por lo tanto, si su código de ciclo tiene más de 8 bytes, no tendrá efecto porque el búfer se reescribe cada ciclo una y otra vez. Aquí, I-cache podría ayudarlo, pero como veo, no hay I-cache en esta MCU.

Es preferible utilizar un temporizador que provenga de un reloj siempre constante, como 32,768 kHz u otra oscilación, para contar los retrasos. Su MCU implementa muchos temporizadores y el RTC, intente usar uno.

void delay(unsigned timertickstowait)
{
  unsigned time0 = get_current_timer_ticks_count();
  while( (unsigned)(get_current_timer_ticks_count() - time0) < timertickstowait )
    { /* do nothing */ }
  return;
}

Si el temporizador que utiliza proviene de un reloj (frecuencia) independiente del reloj de la MCU, la rutina de retardo será prácticamente independiente en su comportamiento de la frecuencia de la MCU y/o los parámetros de acceso Flash.