Hi All, Has someone already been working on this? Well, I did.... To get this working, I would like to ask you some questions. - I guess the ARM interrupt handler must be able to do 'nested' interrupts right? - Does the timer interrupt (the one in atomport.c) ever return? I mean, this interrupt calls the scheduler and that switches to one of the thread routines isn't it? Thanks, Kees.
Hi Kees, Good to hear from you. A user has reported some success on ARM9 but I haven't yet integrated that port into the official tree and completed all automated tests on it. I am planning to work on an ARM7 or ARM9 port myself within the next couple of weeks but meanwhile I'll help out wherever I can. Which ARM device are you using? > - I guess the ARM interrupt handler must be able to do 'nested' > interrupts right? Both of the current ports (ATmega and STM8) currently run in non-nested mode. Changes may be necessary for nested interrupts and I will make any kernel changes necessary for that when I add support for nesting. Meanwhile there is no reason why you would need to use nested interrupts for the benefit of the kernel, only if your application requires it. > - Does the timer interrupt (the one in atomport.c) ever return? I mean, > this interrupt calls the scheduler and that switches to one of the > thread routines isn't it? This will depend on the port, but the way the ATmega and STM8 ports work is that any interrupt handlers (including the timer interrupt) which cause a thread switch, will immediately switch execution to the new thread. The final instructions of the interrupt handler are not executed until the original thread is scheduled back in. This is OK on both of these architectures because it is possible to "complete" an interrupt by regular return calls rather than "return-from-interrupt" calls. There is some detailed discussion of this in the new STM8 port (ports/stm8/atomport-asm.s). There may be more appropriate schemes for other architectures. Please let me know if I can help with any further porting issues. Thanks, Kelvin.
Hi Kelvin, Wow, that is quick! Well, I am working on a port for the ARM1026EJ an embedded core that my company is using in an ASIC. There shouldn't be too much difference between this and an ARM9 though. The ARM port is a little more complicated because of the various processor modes. Either I have to run the whole scheduler in IRQ mode and use the IRQ stack, or switch to system or user mode in the ARM's interrupt handler (In case I go for nested interrupts this is a must anyway.) Anyway, thanks for the explanation. Well, I thought that the timer interrupt would never return and just switch to another thread, so that nested interrupts were a must in order to keep getting timer interrupts. Perhaps I should further check your code to see when and where the timer interrupt returns coz I still don't understand the mechanism on how to switch. When the timer interrupt returns when the original thread is scheduled back in, then something, some interrupt, should cause that. Since interrupts are disabled during the timer interrupt (unless I used nested interrupts) I don't see how is working. Thanks, Kees. > Hi Kees, > > Good to hear from you. > > A user has reported some success on ARM9 but I haven't yet integrated > that port into the official tree and completed all automated tests on > it. I am planning to work on an ARM7 or ARM9 port myself within the > next couple of weeks but meanwhile I'll help out wherever I can. Which > ARM device are you using? > > >> - I guess the ARM interrupt handler must be able to do 'nested' >> interrupts right? >> > Both of the current ports (ATmega and STM8) currently run in > non-nested mode. Changes may be necessary for nested interrupts and I > will make any kernel changes necessary for that when I add support for > nesting. Meanwhile there is no reason why you would need to use nested > interrupts for the benefit of the kernel, only if your application > requires it. > > >> - Does the timer interrupt (the one in atomport.c) ever return? I mean, >> this interrupt calls the scheduler and that switches to one of the >> thread routines isn't it? >> > This will depend on the port, but the way the ATmega and STM8 ports > work is that any interrupt handlers (including the timer interrupt) > which cause a thread switch, will immediately switch execution to the > new thread. The final instructions of the interrupt handler are not > executed until the original thread is scheduled back in. This is OK on > both of these architectures because it is possible to "complete" an > interrupt by regular return calls rather than "return-from-interrupt" > calls. There is some detailed discussion of this in the new STM8 port > (ports/stm8/atomport-asm.s). There may be more appropriate schemes for > other architectures. > > Please let me know if I can help with any further porting issues. > > Thanks, > Kelvin. >
Hi Kees, > Perhaps I should further check your code to see when and where the timer > interrupt returns coz I still don't understand the mechanism on how to > switch. When the timer interrupt returns when the original thread is > scheduled back in, then something, some interrupt, should cause that. > Since interrupts are disabled during the timer interrupt (unless I used > nested interrupts) I don't see how is working. I've made a note to add some more detail to the documentation regarding context switches. But I'll try to give you a flavour now (this applies to the current range of ports, new ports may context switch slightly differently): First of all, cooperative context switches occur with no interrupt at all. If a thread schedules itself out (for example by calling atomTimerDelay() to go to sleep) then the scheduler decides which thread should be up next, and calls archContextSwitch() in order to switch the register context to the new thread. Now consider a thread (Thread B) is asleep on a timer, and is due to be scheduled in when the next timer tick interrupt occurs: * Thread A is currently running * The timer tick interrupt occurs and the scheduler decides during the timer interrupt that Thread B should be woken up. It calls archContextSwitch() to do this. * archContextSwitch saves the context of the previously running thread (Thread A) and restores the context of the new thread (Thread B). On exit from the timer interrupt, Thread B is now running. However the latter instructions of the original interrupt handler are not executed, because context has switched to Thread B during archContextSwitch(). That thread went to sleep using atomTimerDelay() so on exiting archContextSwitch() it goes back up the call stack through atomTimerDelay(). The actual remaining instructions of the original interrupt handler are not executed yet, because the call stack of that interrupt was saved on the interrupted thread's stack (Thread A). When Thread A is eventually scheduled back in, it returns through the interrupt handler call stack. This scheme is used for both the ATmega and STM8 ports - it may not be possible to do it on all architectures. It is possible on both of these architectures because interrupts can be exited much like returning from regular functions. Taking ATmega as an example, the difference between RETI and RET is whether interrupts are re-enabled or not. Therefore you can exit the timer interrupt using just a RET, because the new thread context restore re-enables interrupts as part of its context restore anyway. This is convenient because it reduces the context-switch routine and allows it to be used for both cooperative switches and preemptive (via interrupt) switches. On a cooperative switch it only needs to save those registers which are not saved by the compiler on a function call, because the call to archContextSwitch() is a regular C call. For preemptive switches, the interrupt handler may already have saved some registers, and when it calls out to the C routine archContextSwitch() it will do the same as for cooperative switches (save registers which cannot be clobbered by the called routine). This means that the same small context-switch routine can be used in both cases, reducing development and debug time, as well as avoiding (in the cooperative case) unnecessary register saves where registers are already saved by the compiler on calling out to archContextSwitch(). This is slightly complicated on architectures with separate stacks for interrupts, and I intend to provide an example of this shortly. Different context-switch schemes could be supported in architecture ports while maintaining the same core kernel. I have just started a Cortex M3 port, and intend to keep adding ports which will cover some of the more unusual architectures to provide wider examples for those porting to such architectures. Thanks, Kelvin.