Hay varios problemas aquí:
- AFAIK, solo hay un bit para controlar las interrupciones en un AVR. Por lo tanto, la discusión sobre un control de interrupción más preciso no se aplica a esta CPU.
- El ejemplo ilustra un caso sólido para buscar en el ensamblador el código generado por el compilador .
Debería ser práctico detectar todos los usos de cli to sei con un pequeño script de edición aplicado a la salida objdump (para que sea fácil de monitorear). Un script basado en programas podría resaltar todos los ejemplos de "llamada" entre ellos.
Esto podría mostrar que no hay problema para resolver en su código.
- El control de las interrupciones usando cli () / sei () es la forma más fácil para que un desarrollador asegure el acceso atómico y, por lo tanto, mantenga la consistencia de la memoria, para valores de múltiples bytes en un AVR.
Tener que escribir el ensamblador puede ser tan propenso a errores, o tener un impacto negativo en la optimización, que es un enfoque deficiente. No lo haría hasta que el resultado del paso 1 muestre que hay un problema que resolver.
-
El ejemplo muestra que el compilador ha generado un código para el entero divida insertando una llamada de subrutina para val = 65535U / val
, y esa llamada es el código que se ha 'optimizado' entre cli () a sei ().
Sin embargo , no pruebe que las subrutinas generadas por el usuario se mueven alguna vez al código entre cli () y sei (). Así que esto puede no ser un problema para el caso, potencialmente mucho más significativo.
-
Este ejemplo se soluciona introduciendo una nueva variable volatile :
#define cli() __asm volatile( "cli" ::: "memory" )
#define sei() __asm volatile( "sei" ::: "memory" )
unsigned int ivar;
void test2( unsigned int val ) {
volatile unsigned int val1 = 65535U / val;
cli();
ivar = val1;
sei();
}
El código generado se convirtió en:
92: cf 93 push r28
94: df 93 push r29
96: 00 d0 rcall .+0 ; 0x98 <_Z5test2j+0x6>
98: cd b7 in r28, 0x3d ; 61
9a: de b7 in r29, 0x3e ; 62
9c: bc 01 movw r22, r24
9e: 8f ef ldi r24, 0xFF ; 255
a0: 9f ef ldi r25, 0xFF ; 255
a2: 0e 94 fb 00 call 0x1f6 ; 0x1f6 <__udivmodhi4>
a6: 7a 83 std Y+2, r23 ; 0x02
a8: 69 83 std Y+1, r22 ; 0x01
aa: f8 94 cli <----------------------- switch off interrupts
ac: 89 81 ldd r24, Y+1 ; 0x01
ae: 9a 81 ldd r25, Y+2 ; 0x02
b0: 90 93 01 01 sts 0x0101, r25
b4: 80 93 00 01 sts 0x0100, r24
b8: 78 94 sei <----------------------- switch on interrupts
ba: 0f 90 pop r0
bc: 0f 90 pop r0
be: df 91 pop r29
c0: cf 91 pop r28
c2: 08 95 ret
Esto no es bonito, pero es relativamente fácil y puede ser suficiente. Por supuesto, evitar optimizaciones prematuras; no haga cambios sutiles para controlar el compilador hasta que se vuelva importante, y preferiblemente después de que el código esté funcionando y sea estable.
Verificaría el código generado y esperaría a ver un problema, luego resolvería ese caso específico.
Creo que plantearía un informe de error si mi código de nivel de usuario se mueve entre cli () / sei (). Eso le daría a los desarrolladores del compilador la oportunidad de identificar soluciones o desarrollar arreglos. Los desarrolladores de compiladores tienen la libertad de inventar soluciones, a menudo utilizando pragma's, y pueden responder a un informe de errores ofreciendo una solución sólida.
Mientras tanto, sería más fácil continuar por el camino fácil, en lugar de hacer que el desarrollo sea más difícil, hasta que haya evidencia de un problema importante.