Microchip PIC – How to recover the context saved in interrups or disable the automatic saving of the context

operating systempic

I'm building a dispatcher for PIC microcontrollers to switch tasks saving their contexts.

The dispatcher is triggered when an interrupt occurs. The problem is that the compiler I'm using (C18 V3.x) saves/restores the context automatically when an interrupt occurs, but I don't know how to recover the information saved.

I want to disable the automatic saving of the context and make it by myself, choosing just the registers I want to save.

Part of the ISR's code is:

void isr_dispatcher()
{
    uint8 w_temp, status_temp, bsr_temp;

    INTCONbits.GIE = 0; 

    // save the context
    _asm
        movwf   w_temp, 0
        movff   STATUS_REG, status_temp
        movff   BSR_REG, bsr_temp
    _endasm

    // ...   
}

When an interrupt occurs (TMR0 overflow, in my case), isr_dispatcher begins to execute. The problem is that the compiler translates my isr_dispatcher to this assembly code:

!void isr_dispatcher()
0x1308: MOVFF FSR2H, PREINC1
0x130A: NOP
0x130C: MOVFF FSR1H, FSR2H
0x130E: NOP
0x1310: MOVFF FSR0, PREINC1
0x1312: NOP
0x1314: MOVFF FSR0H, PREINC1
0x1316: NOP
0x1318: MOVFF TBLPTR, PREINC1
0x131A: NOP
0x131C: MOVFF TBLPTRH, PREINC1
0x131E: NOP
0x1320: MOVFF TABLAT, PREINC1
0x1322: NOP
0x1324: MOVFF PROD, PREINC1
0x1326: NOP
0x1328: MOVFF PRODH, PREINC1
0x132A: NOP
0x132C: MOVFF PCLATH, PREINC1
0x132E: NOP
0x1330: LFSR 0, 0x0
0x1332: NOP
0x1334: MOVLW 0x14
0x1336: DECF WREG, W, ACCESS
0x1338: BNC 0x1340
0x133A: MOVFF POSTINC0, PREINC1
0x133C: NOP
0x133E: BRA 0x1336
0x1340: LFSR 0, 0x14
0x1342: NOP
0x1344: MOVLW 0x7
0x1346: DECF WREG, W, ACCESS
0x1348: BNC 0x1350
0x134A: MOVFF POSTINC0, PREINC1
0x134C: NOP
0x134E: BRA 0x1346
0x1350: MOVF POSTINC1, F, ACCESS
0x1352: MOVFF FSR2, POSTINC1
0x1354: NOP
0x1356: MOVFF FSR1, FSR2
0x1358: NOP
0x135A: MOVLW 0x3
0x135C: ADDWF FSR1, F, ACCESS
!{
!    uint8 w_temp, status_temp, bsr_temp;
!    INTCONbits.GIE = 0; 
0x135E: BCF INTCON, 7, ACCESS
!    // save the context
!    _asm
!        movwf   w_temp, 0
0x1360: MOVWF nova_tarefa, ACCESS
!        movff   STATUS_REG, status_temp
0x1362: MOVFF STATUS, status_temp
0x1364: NOP
!        movff   BSR_REG, bsr_temp
0x1366: MOVFF BSR, bsr_temp
0x1368: NOP
!    _endasm

As you can see, the compiler inserts several assembly instructions before my original isr_dispatcher code. I wish the compiler did not do it. Just so I could save the context.

Best Answer

First, this is totally inappropriate to write in anything other than assembler. Messing with parts of the system that the compiler thinks it is managing makes a mess. Performing unnatural acts on the stack, which is necessary for a context switcher, qualifies for the above.

Second, do you really really want a preemptive tasking system? I've done will over 100 PIC projects, and preemptive multitasking hasn't been the right answer yet on such small microcontrollers that perform dedicated functions. Multitasking can be a useful abstraction, particularly when processing a asynchronously received communication stream. However, on small resource-limited systems that perform a fixed function, cooperative multitasking is almost always a better choice. It completely gets around the need for "critical sections" and other types of mutexes around shared data structures since task swaps only happen when you make them happen.

My cooperative task manager is available for free as part of the PIC development tools release at http://www.embedinc.com/pic/dload.htm. Look for files with "task" in their name in the SOURCE > PIC directory within the software installation directory.

Third, if you are using timer 0 overflow to trigger the interrupt that will swap tasks, you'd better be using a decent size prescaler. Otherwise, swapping tasks every 256 instructions will cause the processor to spend most of its time swapping tasks and little actually running tasks. Note that since a PIC 18 has a fixed dedicated hardware call stack, you have to actually copy the existing stack data to a save area, then restore the new stack data from the next task's save area. That takes cycles.