Estoy tratando de optimizar mi RX y TX interrupciones para cumplir con el tiempo de ejecución máximo de 25 ciclos, mientras que las interrupciones están deshabilitadas.
Hasta el momento, he encontrado que el código está optimizado lo suficiente, pero empujando y haciendo estallar serie de registros entre la carga y la descarga __SREG__
supera 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 manera de ser capaz de colocar __SREG__
en lugar más seguro (incluyen tanto empuja como sea posible en la zona 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__" ::); // possible 4 cycle gain if used r0
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 usted puede ver, hay un registro codificado empuja a que mi compilador utilizado, por cierto, se está trabajando en todos, pero no estoy seguro de cómo portátil es.
El único registro que me puede pasar "=r" specificator es r24
y ocurre incluso la máscara y rx0_first_byte
.
Entonces, ¿cómo puedo decirle al compilador a push/pop estos 5 los registros, incluso si van a ser colocados en otros lugares?
¿Cuál es la posibilidad de que el compilador utilizará r19
y r26
en lugar de r18
y r25
?
No quiero volver a escribir toda la ISR en 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 */
);
}