0
\$\begingroup\$

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.

\$\endgroup\$
6
  • \$\begingroup\$ Did you try pulling it off the stack? \$\endgroup\$ – Ignacio Vazquez-Abrams Sep 27 '14 at 3:03
  • 2
    \$\begingroup\$ Which flavour of PIC? In PIC16's (at one time at least, I'm not sure if it's still the case) the stack is completely inaccessible. PIC18's can access their stack (which lives in its own separate memory space) via registers, and PIC24's (and presumably PIC32's) use normal RAM for their stack. All of this is in the assembly commandset for each part/family. \$\endgroup\$ – markt Sep 27 '14 at 3:37
  • \$\begingroup\$ It's for PIC 18 Series. \$\endgroup\$ – user49894 Sep 27 '14 at 5:15
  • \$\begingroup\$ @markt PIC18's cannot access their stack (at least not always), see section 5.1.3 of the PIC18F4620 datasheet for example: "The stack for each register is only one level deep and is neither readable nor writable." \$\endgroup\$ – user17592 Sep 27 '14 at 7:12
  • \$\begingroup\$ @CamilStaps It's been a while since I used a PIC, so I'm losing familiarity a little. I remember (and a quick check of the datasheet confirms) that on the 18F27J13 you could access the "top of stack" data via the TOSU:TOSH:TOSL registers. I don't recall whether it was the same on other PIC18's though - apparently, not all of them anyway! \$\endgroup\$ – markt Sep 27 '14 at 8:33
4
\$\begingroup\$

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.

\$\endgroup\$
8
  • \$\begingroup\$ Hi, Olin. The dispatcher is part of an undergraduate assignment. So my choices are do the assignment or do the assignment, independently of what is better or worse. Until now everything is working, except the context saving. If I can prevent the compiler save the context, my work is almost done. \$\endgroup\$ – user49894 Sep 27 '14 at 23:28
  • 1
    \$\begingroup\$ @std: So use assembler and control exactly what happens when you get into and out of the interrupt. And no, you're not almost done. There are more gotchas with preemptive taskers than you seem to be aware of. Anything you don't save and restore can appear to change asynchronously to task code. Of course you'll save W and STATUS, but what about PRODH/PRODL, FSRs, etc. Where does it end? How much restriction do you want to place on task code? It's not so simple as you seem to think. \$\endgroup\$ – Olin Lathrop Sep 27 '14 at 23:35
  • \$\begingroup\$ Yeah, I know. Which registers need to be saved depends on what the task was doing. Perhaps I will need to create a .tmpdata to every task too. But one step at a time. First I need to turn off the automatic saving of the context by the compiler. \$\endgroup\$ – user49894 Sep 28 '14 at 12:20
  • 1
    \$\begingroup\$ @std: Again, DO THIS IN ASSEMBLER. When doing something like a task switcher, you have to understand the machine and be fluent with the instruction set anyway. This is not a project where you can hide behind a compiler to not have to know the instruction set and the machine's detailed architecture. \$\endgroup\$ – Olin Lathrop Sep 28 '14 at 13:36
  • \$\begingroup\$ Write the dispatcher in assembly would be very hard, because I have to save data in fields of structs (TCBs) and I don't know how to do that in assembly. \$\endgroup\$ – user49894 Sep 28 '14 at 14:58
0
\$\begingroup\$

This answer was written for an older version of the question. From the comments to this answer the intention of the OP became clear, making this answer invalid.

It's not entirely clear to me what you mean. Do you want to restore the context after processing the ISR? Or do you want to access the context in the ISR?

If you just want to restore it in the ISR, that can be done with the retfie (Return from interrupt) command. More information can be found in these slides, especially on page 11.

If you want to access the context from within the ISR, that might be possible, depending on your exact situation. Have a look at section 10.9 of the PIC18F4620 datasheet (I just took one randomly, check your specific datasheet as well):

During interrupts, the return PC address is saved on the stack. Additionally, the WREG, STATUS and BSR registers are saved on the Fast Return Stack. If a fast return from interrupt is not used (see Section 5.3 “Data Memory Organization”), the user may need to save the WREG, STATUS and BSR registers on entry to the Interrupt Service Routine.

I believe "Section 5.3" should really be "Section 5.1.3". If you look there:

A Fast Register Stack is provided for the STATUS, WREG and BSR registers, to provide a “fast return” option for interrupts. The stack for each register is only one level deep and is neither readable nor writable. It is loaded with the current value of the corresponding register when the processor vectors for an interrupt. All interrupt sources will push values into the stack registers. The values in the registers are then loaded back into their associated registers if the RETFIE, FAST instruction is used to return from the interrupt.

As you can see, if the Fast Register Stack is used, you cannot recover the context within the ISR. In the rest of this section it's explained when the Fast Register Stack is not used:

If both low and high-priority interrupts are enabled, the stack registers cannot be used reliably to return from low-priority interrupts. [...] In these cases, users must save the key registers in software during a low-priority interrupt.

So if it's possible in your current setup to enable both priorities (or it is already like this), the PIC doesn't do context saving by itself and you have to do it yourself. That means you have full control over the context.

\$\endgroup\$
6
  • \$\begingroup\$ Is there a way I could disable that automatic context saving on interrupts? I tried to use high and low priorities but the compiler still generates the context saving code. \$\endgroup\$ – user49894 Sep 27 '14 at 17:29
  • \$\begingroup\$ Ah, so it's the compiler that does that? From your question I understood it was the PIC itself. What compiler are you using, and what is your code? (You can edit it in to your question). \$\endgroup\$ – user17592 Sep 27 '14 at 17:30
  • \$\begingroup\$ Question edited. \$\endgroup\$ – user49894 Sep 27 '14 at 17:37
  • \$\begingroup\$ When I remove the "#pragma interrupt isr_function_name" directive from above of the isr routine, the compiler generates a little less code (apparently does not save the context), but still generates this before the code of the isr: MOVFF FSR2, POSTINC1 NOP MOVFF FSR1, FSR2 NOP MOVLW 0x3 ADDWF FSR1, F, ACCESS \$\endgroup\$ – user49894 Sep 27 '14 at 17:51
  • \$\begingroup\$ @stdio.h thank you, could you now also give us the relevant code? \$\endgroup\$ – user17592 Sep 27 '14 at 17:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy