c assembly arm embedded armcc

¿Cómo prevenir la expansión de las instrucciones LDM/STM en ARM Compiler 5 armcc inline assembler?



assembly embedded (1)

Entonces, me pregunto si hay una forma de generar LDM / STM correctamente sin perder rendimiento. Pudimos hacer esto en GCC, pero no encontramos ninguna solución para armcc.

Un poco sobre las optimizaciones del compilador. La asignación de registros es uno de sus trabajos más difíciles. El corazón de la generación de código de cualquier compilador probablemente esté presente cuando asigna registros de CPU físicos. La mayoría de los compiladores están utilizando la asignación estática única o SSA para cambiar el nombre de sus variables ''C'' en un grupo de pseudo variable (o variables de orden de tiempo).

Para que su STMIA y LDMIA funcionen, necesita que las cargas y las tiendas sean coherentes. Es decir, si es stmia [rx], {r3,r7} y una restauración como ldmia [rx], {r4,r8} con la asignación ''r3'' al nuevo ''r4'' y la asignación ''r7'' almacenada al restaurado ''r8''. Esto no es simple para cualquier compilador para implementar genéricamente ya que las variables ''C'' se asignarán de acuerdo a las necesidades. Diferentes versiones de la misma variable pueden estar en diferentes registros. Para hacer que stm/ldm funcione esas variables deben asignarse de manera que el registro se incremente en el orden correcto. Es decir, para la ldmia anterior, si el compilador quiere el r7 almacenado en r0 (¿tal vez un valor de retorno?), No hay forma de que cree una buena instrucción ldm sin generar código adicional.

Es posible que haya obtenido gcc para generar esto, pero probablemente fue suerte. Si continúa con solo gcc, probablemente encontrará que no funciona tan bien.

Ver: ldm / stm y gcc para problemas con GCC stm / ldm.

Tomando tu ejemplo,

inline void STMIA2(uint32_t addr, uint32_t w0, uint32_t w1) { __asm { STMIA addr!, { w0, w1 } } }

El valor de inline es que todo el cuerpo de la función se puede poner en el código. La persona que llama puede tener w0 y w1 en los registros R8 y R4. Si la función no está en inline , entonces la compilación debe colocarlos en R1 y R2, pero puede haber generado movimientos adicionales. Es difícil para cualquier compilador cumplir los requisitos de ldm/stm genérica.

Esto afecta el rendimiento ya que HW lo usamos optimizado para el procesamiento de ráfagas. También esto rompe la corrección funcional porque HW que usamos toma en consideración la secuencia de palabras e ignora las compensaciones (pero el compilador cree que es seguro cambiar el orden de las instrucciones).

Si el hardware es un periférico esclavo sin memoria particular en el bus, puede envolver la funcionalidad para escribir en este esclavo en un contenedor externo y forzar la asignación del registro (ver AAPCS ) para que ldm/stm funcione. Esto dará como resultado un golpe de rendimiento que podría ser mitigado por algún ensamblador personalizado en el controlador del dispositivo.

Sin embargo, parece que el dispositivo podría ser memoria? En este caso, tienes un problema. Normalmente, los dispositivos de memoria como este solo usarán un caché? Si su CPU tiene una MPU (unidad de protección de memoria) y puede habilitar los datos y el caché de códigos, entonces puede resolver este problema. Las líneas de caché siempre serán accesos de ráfaga. Solo se debe tener cuidado en el código para configurar la MPU y la memoria caché de datos. OPs Cortex-M0 + no tiene memoria caché y los dispositivos no son de memoria por lo que esto no será posible (ni necesario).

Si su dispositivo es memoria y no tiene caché de datos, entonces su problema probablemente no se resuelva (sin un esfuerzo masivo) y necesita hardware diferente. O puede envolverlo como el dispositivo periférico y obtener un golpe de rendimiento; perdiendo los beneficios del acceso aleatorio del dispositivo de memoria.

Estoy intentando generar accesos de ráfaga de bus AXI usando instrucciones STM / LDM en ensamblado en línea en un archivo .c compilado con ARM Compiler 5 armcc.

inline void STMIA2(uint32_t addr, uint32_t w0, uint32_t w1) { __asm { STMIA addr!, { w0, w1 } } }

Pero ARM Compiler armcc User Guide, párrafo 7.18 dice: "Todas las instrucciones LDM y STM se expanden en una secuencia de instrucciones LDR y STR con efecto equivalente. Sin embargo, el compilador puede recombinar posteriormente las instrucciones separadas en un LDM o STM durante la optimización. "

Y eso es lo que realmente sucede en la práctica, LDM / STM se expanden en un conjunto de LDR / STR en algunos casos y el orden de estas instrucciones es arbitrario. Esto afecta el rendimiento ya que HW lo usamos optimizado para el procesamiento de ráfagas. También esto rompe la corrección funcional porque HW que usamos toma en consideración la secuencia de palabras e ignora las compensaciones (pero el compilador cree que es seguro cambiar el orden de las instrucciones).

Para resolver esto, es posible utilizar el ensamblador integrado en lugar del ensamblador en línea, pero esto genera llamadas a funciones adicionales, lo que afecta el rendimiento.

Entonces, me pregunto si hay una forma de generar LDM / STM correctamente sin perder rendimiento. Pudimos hacer esto en GCC, pero no encontramos ninguna solución para armcc.

CPU objetivo: Cortex M0 + (ARMv6-M).

Editar: los dispositivos esclavos son todos dispositivos en chip, la mayoría de ellos no son dispositivos de memoria. Para cada registro de esclavo sin memoria que admita la región de acceso de ráfaga del espacio de direcciones está reservado (por ejemplo [0x10000..0x10100]), no estoy completamente seguro de por qué, tal vez la CPU o el bus no admiten fijo (no incremental ) direcciones. HW ignora las compensaciones dentro de esta región. La solicitud completa puede ser de 16 bytes, por ejemplo, y la primera palabra de la solicitud completa es la primera palabra escrita (incluso si el desplazamiento no es cero).