As far as I know, you can't really play with the registers in C like that. Of course you could write asm functions to get and set the registers (or C functions with inline assembly) but I suspect the compiler would undo all your magic register work when you left the interrupt/function. I would really STRONGLY recommend against trying to modify registers like that from C code, as the C compiler is built around the assumption that it is in control of the registers - I feel like trying to work against the compiler would be a bad idea.
I don't really see much value to storing the registers to a struct/array rather than a stack, but you could do it. What you REALLY shouldn't do however is return to a new function/context using an old function/context's stack. The stack is used for local storage and as such may be arbitrarily modified during a function as the C compiler sees fit (to backup clobbered registers, for local functions, parameter passing etc). Returning into a new function/context with an old stack will give you strange behaviour at best, most likely will result in corrupted data and crash as soon as that function returns and the PC is set to a random value found on the stack. So like I said, if you want to do context switching you will need one stack for each context. There is really no way around that. You should treat each context as a completely separate C program, and be very aware of strangeness like trying to share volatile global structures between the two contexts (make sure to write/read multiword types/structs from critical sections to avoid data corruption). - Wayne ----- Original Message ----- From: Sergio Campamá [mailto:scamp...@ing.puc.cl] Sent: Wednesday, December 07, 2011 08:56 PM To: Wayne Uroda Cc: Peter Bigot <big...@acm.org>; mspgcc-users@lists.sourceforge.net <mspgcc-users@lists.sourceforge.net> Subject: Re: [Mspgcc-users] Enter LPM4 without missing the wakeup interrupt Thanks for the tips Wayne! Talking about the context switch problem, could I obtain the registers in C code? What I was thinking was not doing the context save into other stacks, but into C structs, preallocated on compile time (this would limit the amount of simultaneous context switching). And use the same stack pointer for the new tasks… Best regards, --------------------------------------- Sergio Campamá sergiocamp...@gmail.com On Dec 7, 2011, at 12:13 AM, Wayne Uroda wrote: > When an interrupt is generated, the hardware pushes the PC and then the SR > onto the stack. The hardware then clears the SR and loads PC with the > appropriate interrupt address. > > In MSPGCC, the first thing the compiler does on entry to an interrupt routine > is push any registers clobbered during the interrupt routine to the stack. > Then the interrupt work is done, then the registers are popped in the reverse > order, then the RETI instruction causes the machine to pop back the SR and > then the original PC is popped. > > So if you want to do a context switch in your interrupt routine, you should > push ALL the registers to the stack as you have indicated. You will have to > break out some assembly code to do this. You will also (I'm guessing) need to > stop GCC from generating the interrupt related pushes and pops. Probably best > to write this whole interrupt handler in assembly so you can be sure what is > going on. > > So, you will need to do the following (Note that R0 to R3 are PC, SP, SR, and > CG respectively) (this code surely doesn't work verbatim, it is just an > example) > > /* start of ISR */ > /* first save all the general purpose registers to the stack */ > push r15 > push r14 > ... repeat > push r5 > push r4 > > /* now we can do a context switch by swapping the SP with another VALID > context */ > /* first back up the old SP to some address, then load in the new SP */ > /* choosing the new context can be simple or complex, this depends on the > type of scheduler you wish to use */ > mov r1, &oldSP > mov &newSP, r1 > > /* should clear whatever interrupt flag got us to this ISR in the first place > */ > TODO > > /* now we are operating in the new context, we should restore all registers > and return from the interrupt */ > pop r4 > pop r5 > ... repeat > pop r14 > pop r15 > reti > /* end of ISR */ > > This means that your new context has its own stack, so make sure that each > context (stack) is big enough for all the functions that they need to execute > between task switching. > Note where I say "VALID context" - this must be a stack which already has > registers r4 to r15 as well as the SR and PC to return to - otherwise you > will get stack underrun when you execute the 12 pops and then the machine > executes the 2 pops (SR and PC). > > The smallest possible size a context could be is then 14 words or 28 bytes, > but this doesn't leave any room for the stack to grow during your actual > function. > > If you want to perform only one function in this other context, maybe looking > into nested interrupts would be better, as this scheme above will probably > take a fair bit of effort to get working. > > There are plenty of free RTOS around on the internet, I have not used any on > the MSP430 though, but they would have done this work already. > > As for link scripts, start with the GNU LD manual. Also look at the existing > link scripts under the 'ldscripts' directory in your mspgcc installation. > > Your example of a system which uses a function which can be replaced > on-the-fly (in flash I assume) could be archived by reserving a block of RAM > (just a global array for example, word aligned), and at startup copy your > function into this block. Then if the system receives a new function it can > overwrite the old function in ram and execute it. The functions should share > the same return type and parameters as you say, to make calling it from C > easy (call it through a function pointer). > > Of course if you want it to be stored through a reset then yes, you will need > flash, and yes you probably will need to write your own linker script to put > the function at a known address and keep other functions out of its area. > Keep in mind the smallest memory you can erase is a segment so you probably > will want to put a whole segment (or multiple segments) aside for your > loadable module. > > - Wayne > > -----Original Message----- > From: Sergio Campamá [mailto:sergiocamp...@gmail.com] On Behalf Of Sergio > Campamá > Sent: Wednesday, 7 December 2011 12:34 PM > To: Wayne Uroda > Cc: Peter Bigot; mspgcc-users@lists.sourceforge.net > Subject: Re: [Mspgcc-users] Enter LPM4 without missing the wakeup interrupt > > Hey, maybe I'm straying a bit from the conversation, but > > 1. Is there a way to obtain (and set) the registers? I want to try a feature > where I create preemptive "threads", in which after certain interrupts, I > will save the current context (registers) and the instruction line I was > executing (which is already in the registers, the PC), and start executing > from another address.. And when I'm done with that, reload the PC and all the > other registers and resume working on that "thread"... I'm guessing that 8 * > 16 = 128 bytes would be the least memory I would need for each context save. > Is it not? > > 2. Where can I find tutorials or more info to read about custom linking > scripts? I also want to try a feature where I have a base system setup and a > special memory area reserved for a module, and when the base system receives > a new module (with a flash memory proxy or whatever), it replaces the old > one... I'm assuming the entry point for that module would have to be the same > function name and the same memory address (is it not?) > > Best regards, > --------------------------------------- > Sergio Campamá > sergiocamp...@gmail.com > > > > > On Dec 6, 2011, at 11:10 PM, Wayne Uroda wrote: > >> Thanks. That is what I expected the wakeup keyword did so I am happy with >> using it. >> >> I personally only use the critical and wakeup keywords. Of course putting >> dint and eint calls at the start and end of the function is easy, but I >> prefer critical as it saves the current state of GIE on the stack and pops >> it on exit (whereas hand coding dint eint will always leave your function >> with interrupts enabled). >> >> There is probably some easy macro I don't know about to push and then pop >> the status register but if you push and then forget to pop I imagine you >> might rip your hair out for a while trying to work out what is going on :) >> >> Our platform is fully hand coded from the ground up (custom C startup, >> custom linker scripts, custom ELF based tools) to allow for custom >> bootloader / firmware loading. So the critical and wakeup keywords will be >> the least of somebody's worries should they ever need to port the code to >> IAR ;) >> >> - Wayne >> >> -----Original Message----- >> From: pabi...@gmail.com [mailto:pabi...@gmail.com] On Behalf Of Peter Bigot >> Sent: Wednesday, 7 December 2011 11:59 AM >> To: Wayne Uroda >> Cc: mspgcc-users@lists.sourceforge.net >> Subject: Re: [Mspgcc-users] Enter LPM4 without missing the wakeup interrupt >> >> On Tue, Dec 6, 2011 at 5:53 PM, Wayne Uroda <wayne.ur...@grabba.com> wrote: >>> Thanks for the discussion - I apologise for my code sample having flaws, it >>> isn't production code just something I threw together to illustrate the >>> point. In my case I am only interested in the level of the pin (sleep when >>> it is low, awake when it is high) and so I am not concerned with missing >>> edges other than the critical edge which brings me out of LPM4. I am aware >>> that I still need to be careful of the order in which I enable the >>> interrupt/clear the ifg and disable interrupts before finally checking the >>> level of the pin and entering LPM4 if appropriate. >>> >>> One thing that I have never done is use the on_exit macro - I use the >>> wakeup keyword instead - I'm guessing there are potential problems with the >>> wakeup keyword? >> >> The wakeup function attribute in mspgcc is equivalent to having >> "bic_status_register_on_exit(0xF0)" in your interrupt handler (and >> LPM4_bits is 0xF0). There's nothing intrinsically wrong with it >> except that it's opaque, and will only do exactly that operation, so >> you couldn't (for example) clear GIE on exit using it. >> >> Personally I never remember what "signal", "wakeup", "reentrant", and >> "critical" as function attributes mean exactly, and if I encounter >> them I have to go read the compiler source to remind me what they do. >> As far as I know they're also specific to mspgcc, so people who use >> IAR or CCS won't understand what you're doing either. >> >> Peter >> >>> >>> - Wayne >>> >>> -----Original Message----- >>> From: JMGross [mailto:msp...@grossibaer.de] >>> Sent: Wednesday, 7 December 2011 12:47 AM >>> To: MSPGCC mailing list, >>> Subject: Re: [Mspgcc-users] Enter LPM4 without missing the wakeup interrupt >>> >>> >>> Hello Wayne, >>> >>> you correctly detected a possible problem in your code, which from my >>> experience really is an exception. Most 'coders' don't >>> care for realtime influences and then wonder why the code doesn't work (or >>> use a watchdog do reset the CPU when the code >>> unexpectedly 'hangs'. >>> >>> A generic solution to your problem has already beenb pinted out. Set LPM >>> and GIE in the same instruction. >>> Both, the LPM bits as well as GIE are located in the status register, so it >>> comes down to an atomic operation: >>> BIS #(LPM4|GIE), R2 >>> >>> In this case, if an interrupt happened before GIE is set and LMP is >>> entered, the following will happen: >>> GIE/LPM is set. >>> LMP is exited, since an ISR is pending. (I'm not sure whether the DCO/XT >>> wakeup time does apply in this case) >>> the instruction after the BIS is executed (it aslready had been fetched >>> before the BIS was executed, hence the usual NOP after an LPM entry) >>> the status register including the GIE and LPM bits is stored on the stack >>> GIE ans LPM bits are cleared >>> the ISR is executed. >>> >>> If the ISR doesn't manipulate the stored status register copy on the stack, >>> then when exiting the ISR, teh SR is restord and LPM/GIE are >>> in effect again. >>> >>> So if you clear the LPM bits inside the ISR (using the proper 'on_exit' >>> macro!), MSP won't return to LPM. >>> >>> However, your code example has several more flaws. >>> - When your ISR does not clear the IFG bit for th einterrupt, the ISR will >>> be re-entered as soon as it exits. >>> If it does, the port1.ifg.pin1=0 assignment will clear a possible next >>> interrupt, and you'll miss it. >>> - you enable porti.ie.pin1 in every loop. It stays enabled (unless the ISR >>> disables it) and interrupts may >>> happen all the time (or after this point) independently of LPM4. You should >>> clear GIE on exit of the ISR too, >>> so when main is awakened, no further interrupts are handled unless you want >>> them again. >>> - you check for port1.in-.pin1. But even if an interrupt happened, P1.1 >>> might have gone low again in the meantime. >>> It's better to set a global flag inside the ISR (don't forget to mark it >>> volatile) and check this. >>> - depending on the compiler capabilities, the use of bitfields may result >>> in less efficient code. >>> That's one reason why bitfields for the many registers (especially ports) >>> have been removed from the 'official' >>> header files. Especially since every bit operation is a RMW >>> (read-modify-write) operation on the whole register >>> (which may have side-effects that are easily missed when one jus tlooks on >>> the bit oepration). >>> Also, since the registers are 'volatile', manipulation of multiple bits in >>> the same register results in multiple >>> RMW operations. Due to the volatile nature of the registers, the compiler >>> is not allowed to group them. >>> (the sequential access might be intentional). >>> >>> If you want to implement a debounce feature, a typical way to do it is to >>> start a timer inside the port ISR >>> and disable port interrupts (PxIE=0). >>> When the timer expires, the tiemr ISR will check for the current port pin >>> state, re-enable port interrupts and >>> depending on the port pin state will set a global flag and end LPM. >>> It gets a bit more tricky if you have multiple interrupt pins, but there >>> have been implementations discussed >>> and posted in the 'official' MSP forum: e2e.ti.com. >>> >>> JMGross >>> >>> >>> ----- Ursprüngliche Nachricht ----- >>> Von: Wayne Uroda >>> An: mspgcc-users@lists.sourceforge.net >>> Gesendet am: 06 Dez 2011 00:40:59 >>> Betreff: [Mspgcc-users] Enter LPM4 without missing the wakeup interrupt >>> >>> I have a question which isn't technically related to MSPGCC (more of a >>> msp430 question) but I thought one of you smart people >>> might know. >>> >>> Imagine the following scenario: >>> >>> /* 1*/ while (1) >>> /* 2*/ { >>> /* 3*/ if (!port1.in.pin1) >>> /* 4*/ { >>> /* 5*/ // Enable interrupt (rising edge) for pin 1.1 >>> /* 6*/ port1.ies.pin1 = 0; >>> /* 7*/ port1.ifg.pin1 = 0; >>> /* 8*/ port1.ie.pin1 = 1; >>> /* 9*/ >>> /*10*/ // Enter sleep mode, but only if the pin is still >>> not high >>> /*11*/ if (!port1.in.pin1) >>> /*12*/ { >>> /*13*/ LPM4(); >>> /*14*/ } >>> /*15*/ } >>> /*16*/ >>> /*17*/ // Awake >>> /*18*/ // Do real work here >>> /*19*/ } >>> >>> The ISR for port1 interrupt just wakes up the processor from LPM4 and >>> clears the IFG for pin 1.1. >>> >>> The problem I see is that there is a small window (between the execution of >>> line 11 and line 13) where pin1.1 can go high, have >>> the ISR handled and the IFG cleared, and then the system can incorrectly go >>> into LPM4 even though pin1.1 is high. >>> >>> My thoughts are that the only way around this is to avoid using LPM4 and >>> poll the state of pin 1.1, which is what I have done in >>> previous designs. As far as I know there is no way to atomically enter LPM4 >>> and enable interrupts so that the pending pin1.1 >>> IFG can be handled AFTER entering LPM4, thus bringing the system out of >>> LPM4. >>> >>> Has anybody come up against this? Is using LPM3 the best/only workaround? >>> >>> I am using 1 family chips, MSP430F148 in particular. >>> >>> Thanks, >>> >>> - Wayne >>> >>> ------------------------------------------------------------------------------ >>> All the data continuously generated in your IT infrastructure >>> contains a definitive record of customers, application performance, >>> security threats, fraudulent activity, and more. Splunk takes this >>> data and makes sense of it. IT sense. And common sense. >>> http://p.sf.net/sfu/splunk-novd2d >>> _______________________________________________ >>> Mspgcc-users mailing list >>> Mspgcc-users@lists.sourceforge.net >>> https://lists.sourceforge.net/lists/listinfo/mspgcc-users >>> >>> >>> ------------------------------------------------------------------------------ >>> Cloud Services Checklist: Pricing and Packaging Optimization >>> This white paper is intended to serve as a reference, checklist and point of >>> discussion for anyone considering optimizing the pricing and packaging model >>> of a cloud services business. Read Now! >>> http://www.accelacomm.com/jaw/sfnl/114/51491232/ >>> _______________________________________________ >>> Mspgcc-users mailing list >>> Mspgcc-users@lists.sourceforge.net >>> https://lists.sourceforge.net/lists/listinfo/mspgcc-users >>> >>> ------------------------------------------------------------------------------ >>> Cloud Services Checklist: Pricing and Packaging Optimization >>> This white paper is intended to serve as a reference, checklist and point of >>> discussion for anyone considering optimizing the pricing and packaging model >>> of a cloud services business. Read Now! >>> http://www.accelacomm.com/jaw/sfnl/114/51491232/ >>> _______________________________________________ >>> Mspgcc-users mailing list >>> Mspgcc-users@lists.sourceforge.net >>> https://lists.sourceforge.net/lists/listinfo/mspgcc-users >> >> ------------------------------------------------------------------------------ >> Cloud Services Checklist: Pricing and Packaging Optimization >> This white paper is intended to serve as a reference, checklist and point of >> discussion for anyone considering optimizing the pricing and packaging model >> of a cloud services business. Read Now! >> http://www.accelacomm.com/jaw/sfnl/114/51491232/ >> _______________________________________________ >> Mspgcc-users mailing list >> Mspgcc-users@lists.sourceforge.net >> https://lists.sourceforge.net/lists/listinfo/mspgcc-users > ------------------------------------------------------------------------------ Cloud Services Checklist: Pricing and Packaging Optimization This white paper is intended to serve as a reference, checklist and point of discussion for anyone considering optimizing the pricing and packaging model of a cloud services business. Read Now! http://www.accelacomm.com/jaw/sfnl/114/51491232/ _______________________________________________ Mspgcc-users mailing list Mspgcc-users@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mspgcc-users