Estoy tratando de optimizar mis interrupciones de RX y TX para cumplir con el tiempo máximo de ejecución de 25 ciclos mientras las interrupciones están desactivadas.
Hasta ahora, he encontrado que el código está lo suficientemente optimizado, pero al presionar y abrir series de registros entre la carga y la descarga, __SREG__
excede el límite de tiempo.
272: 80 91 24 01 lds r24, 0x0124
276: 8f 5f subi r24, 0xFF ; 255
278: 8f 71 andi r24, 0x1F ; 31
27a: 90 91 c6 00 lds r25, 0x00C6
27e: 20 91 25 01 lds r18, 0x0125
282: 28 17 cp r18, r24
284: 39 f0 breq .+14 ; 0x294 <__vector_18+0x30>
286: e8 2f mov r30, r24
288: f0 e0 ldi r31, 0x00 ; 0
28a: ea 5d subi r30, 0xDA ; 218
28c: fe 4f sbci r31, 0xFE ; 254
28e: 90 83 st Z, r25
290: 80 93 24 01 sts 0x0124, r24
La única forma de poder colocar __SREG__
en la ubicación más segura (incluir la mayor cantidad posible de empujes en el área inconsciente) fue inline asm.
Aquí está mi código actual:
ISR(RX0_INTERRUPT, ISR_NAKED)
{
//push
asm volatile("push r31" ::); // table pointer
asm volatile("push r30" ::); // table pointer
asm volatile("push r25" ::); // received character
asm volatile("push r18" ::); // once compared to r24 -> rx0_first_byte
asm volatile("push r24" ::); // most stuff is executed in r24
asm volatile("in r24,__SREG__" ::); // -
asm volatile("push r24" ::); // but one byte more on stack
register uint8_t tmp_rx_last_byte = (rx0_last_byte + 1) & RX0_BUFFER_MASK;
register uint8_t tmp = UDR0_REGISTER;
if(rx0_first_byte != tmp_rx_last_byte)
{
rx0_buffer[tmp_rx_last_byte] = tmp;
rx0_last_byte = tmp_rx_last_byte;
}
//pop
asm volatile("pop r24" ::);
asm volatile("out __SREG__,r24" ::);
asm volatile("pop r24" ::);
asm volatile("pop r18" ::);
asm volatile("pop r25" ::);
asm volatile("pop r30" ::);
asm volatile("pop r31" ::);
reti();
}
Como puede ver, hay un código de inserción codificado que mi compilador usó, por cierto que está funcionando, pero no estoy seguro de cuán portátil es.
El único registro que puedo obtener con el especificador "= r" es r24
y está sucediendo incluso para mask y rx0_first_byte
.
Entonces, ¿cómo puedo decirle al compilador que presione / abra estos 5 registros, incluso si se colocarán en otro lugar?
¿Qué posibilidad hay de que el compilador utilice r19
y r26
en lugar de r18
y r25
?
No quiero volver a escribir todo el ISR en el ensamblador.
EDIT: gracias por todas las sugerencias, finalmente reescribí ISR en asm
ISR(RX0_INTERRUPT, ISR_NAKED)
{
asm volatile("\n\t" /* 5 ISR entry */
"push r31 \n\t" /* 2 */
"push r30 \n\t" /* 2 */
"push r25 \n\t" /* 2 */
"push r24 \n\t" /* 2 */
"push r18 \n\t" /* 2 */
"in r18, __SREG__ \n\t" /* 1 */
"push r18 \n\t" /* 2 */
/* read byte from UDR register */
"lds r25, %M[uart_data] \n\t" /* 2 */
/* load globals */
"lds r24, (rx0_last_byte) \n\t" /* 2 */
"lds r18, (rx0_first_byte) \n\t" /* 2 */
/* add 1 & mask */
"subi r24, 0xFF \n\t" //??? /* 1 */
"andi r24, %M[mask] \n\t" /* 1 */
/* if head == tail */
"cp r18, r24 \n\t" /* 1 */
"breq L_%= \n\t" /* 1/2 */
"mov r30, r24 \n\t" /* 1 */
"ldi r31, 0x00 \n\t" /* 1 */
"subi r30, lo8(-(rx0_buffer))\n\t" /* 1 */
"sbci r31, hi8(-(rx0_buffer))\n\t" /* 1 */
"st Z, r25 \n\t" /* 2 */
"sts (rx0_last_byte), r24 \n\t" /* 2 */
"L_%=:\t"
"pop r18 \n\t" /* 2 */
"out __SREG__ , r18 \n\t" /* 1 */
"pop r18 \n\t" /* 2 */
"pop r24 \n\t" /* 2 */
"pop r25 \n\t" /* 2 */
"pop r30 \n\t" /* 2 */
"pop r31 \n\t" /* 2 */
"reti \n\t" /* 5 ISR return */
: /* output operands */
: /* input operands */
[uart_data] "M" (_SFR_MEM_ADDR(UDR0_REGISTER)),
[mask] "M" (RX0_BUFFER_MASK)
/* no clobbers */
);
}
ACTUALIZACIÓN:
Después de algunas pruebas, descubrí que las interrupciones están deshabilitadas antes de ingresar al controlador ISR, no después de descargar __SREG__
como se me sugirió anteriormente.
La única forma es globalizar los registros como se sugiere ndim, o usar el siguiente código:
ISR(RX0_INTERRUPT, ISR_NAKED)
{
asm volatile("\n\t" /* 4 ISR entry */
"push r0 \n\t" /* 2 */
"in r0, __SREG__ \n\t" /* 1 */
"push r31 \n\t" /* 2 */
"push r30 \n\t" /* 2 */
"push r25 \n\t" /* 2 */
"push r24 \n\t" /* 2 */
"push r18 \n\t" /* 2 */
/* read byte from UDR register */
"lds r25, %M[uart_data] \n\t" /* 2 */
#ifdef USART_UNSAFE_RX_INTERRUPT // enable interrupt after satisfying UDR register
"sei \n\t" /* 1 */
#endif
/* load globals */
"lds r24, (rx0_last_byte) \n\t" /* 2 */
"lds r18, (rx0_first_byte) \n\t" /* 2 */
/* tmp_rx_last_byte = (rx0_last_byte + 1) & RX0_BUFFER_MASK */
"subi r24, 0xFF \n\t" /* 1 */
"andi r24, %M[mask] \n\t" /* 1 */
/* if(rx0_first_byte != tmp_rx_last_byte) */
"cp r18, r24 \n\t" /* 1 */
"breq .+14 \n\t" /* 1/2 */
/* rx0_buffer[tmp_rx_last_byte] = tmp */
"mov r30, r24 \n\t" /* 1 */
"ldi r31, 0x00 \n\t" /* 1 */
"subi r30, lo8(-(rx0_buffer))\n\t" /* 1 */
"sbci r31, hi8(-(rx0_buffer))\n\t" /* 1 */
"st Z, r25 \n\t" /* 2 */
/* rx0_last_byte = tmp_rx_last_byte */
"sts (rx0_last_byte), r24 \n\t" /* 2 */
#ifdef USART_UNSAFE_RX_INTERRUPT
"cli \n\t" /* 1 */
#endif
"pop r18 \n\t" /* 2 */
"pop r24 \n\t" /* 2 */
"pop r25 \n\t" /* 2 */
"pop r30 \n\t" /* 2 */
"pop r31 \n\t" /* 2 */
"out __SREG__ , r0 \n\t" /* 1 */
"pop r0 \n\t" /* 2 */
"reti \n\t" /* 4 ISR return */
: /* output operands */
: /* input operands */
[uart_data] "M" (_SFR_MEM_ADDR(UDR0_REGISTER)),
[mask] "M" (RX0_BUFFER_MASK)
/* no clobbers */
);
}