Secciones críticas en Cortex-M3

10

Me pregunto un poco acerca de la implementación de secciones de código críticas en un Cortex-M3 donde no se permiten excepciones debido a restricciones de tiempo o problemas de concurrencia.

En mi caso, tengo un LPC1758 y tengo un transceptor TI CC2500 a bordo. El CC2500 tiene pines que se pueden usar como líneas de interrupción para datos en el búfer de RX y espacio libre en el búfer de TX.

Como ejemplo, quiero tener un búfer de TX en la SRAM de mi MCU y cuando haya espacio libre en el búfer de TX del transceptor, quiero escribir estos datos allí. Pero la rutina que coloca datos en el búfer SRAM obviamente no puede ser interrumpida por la interrupción de espacio libre en TX. Entonces, lo que quiero hacer es desactivar temporalmente las interrupciones mientras se realiza este procedimiento de llenado de este búfer, pero se ejecutan las interrupciones durante este procedimiento una vez que finaliza.

¿Cómo se hace esto mejor en Cortex-M3?

    
pregunta Emil Eriksson

3 respuestas

11

El Cortex M3 admite un par útil de operaciones de operaciones (también comunes en muchas otras máquinas) llamadas "Load-Exclusive" (LDREX) y "Store-Exclusive" (STREX). Conceptualmente, la operación LDREX realiza una carga, también establece algún hardware especial para observar si la ubicación que se cargó podría estar escrita por otra cosa. La ejecución de un STREX a la dirección utilizada por el último LDREX hará que esa dirección se escriba solo si nada más lo escribió primero . La instrucción STREX cargará un registro con 0 si la tienda tuvo lugar, o 1 si fue abortada.

Tenga en cuenta que STREX es a menudo pesimista. Hay una variedad de situaciones en las que puede decidir no realizar la tienda, incluso si la ubicación en cuestión no hubiera sido tocada. Por ejemplo, una interrupción entre un LDREX y STREX hará que STREX suponga que la ubicación que se está observando podría haber sido afectada. Por esta razón, generalmente es una buena idea minimizar la cantidad de código entre LDREX y STREX. Por ejemplo, considere algo como lo siguiente:

inline void safe_increment(uint32_t *addr)
{
  uint32_t new_value;
  do
  {
    new_value = __ldrex(addr) + 1;
  } while(__strex(new_value, addr));
}

que compila a algo como:

; Assume R0 holds the address in question; r1 trashed
lp:
  ldrex r1,[r0]
  add   r1,r1,#1
  strex r1,r1,[r0]
  cmp   r1,#0  ; Test if non-zero
  bne   lp
  .. code continues

La gran mayoría de las veces que se ejecuta el código, no ocurrirá nada entre el LDREX y el STREX para "molestarlos", por lo que el STREX tendrá éxito sin más dilación. Sin embargo, si ocurre una interrupción inmediatamente después de la instrucción LDREX o ADD, el STREX no realizará el almacenamiento, sino que el código volverá a leer el valor (posiblemente actualizado) de [r0] y calculará un nuevo valor incrementado basado en eso.

El uso de LDREX / STREX para realizar operaciones como safe_increment hace posible no solo administrar las secciones críticas, sino también en muchos casos para evitar la necesidad de ellas.

    
respondido por el supercat
4

Parece que necesita algunos búferes circulares o FIFO en su software MCU. Al rastrear dos índices o punteros en la matriz para leer y escribir, puede tener tanto el primer plano como el segundo plano para acceder al mismo búfer sin interferencias.

El código de primer plano es libre de escribir en el búfer circular en cualquier momento. Inserta datos en el puntero de escritura, luego incrementa el puntero de escritura.

El código de fondo (manejo de interrupciones) consume datos del puntero de lectura e incrementa el puntero de lectura.

Cuando los punteros de lectura y escritura son iguales, el búfer está vacío y el proceso en segundo plano no envía datos. Cuando el búfer está lleno, el proceso en primer plano se niega a escribir más (o puede sobrescribir los datos antiguos, según sus necesidades).

El uso de búferes circulares para desacoplar a los lectores y escritores debería eliminar la necesidad de deshabilitar las interrupciones.

    
respondido por el Toby Jaffey
2

No puedo recordar la ubicación exacta pero en las bibliotecas que vienen de ARM (No TI, ARM, debería estar bajo CMSIS o algo así, uso ST, pero recuerdo haber leído en alguna parte que este archivo provino de ARM, por lo que debería tener también) hay una opción de desactivación de interrupción global. Es una llamada de función. (No estoy en el trabajo, pero mañana buscaré la función exacta). Lo resumiría con un bonito nombre en su sistema y deshabilitaría las interrupciones, haría lo suyo y habilitaría de nuevo. Dicho esto, la mejor opción sería implementar un semáforo o una estructura de cola en lugar de una inhabilitación de interrupción global.

    
respondido por el Ktc

Lea otras preguntas en las etiquetas