Electronic – Multithreading on AVR

avrinterruptsthread

If I have an AVR micro controller and have an ISR coming up about every 100 micro seconds or so, can I change the stack pointer in the ISR and then fake multi threading?

Psuedo code:

uint8_t currentThread = 0;
void* process0StackPointer = 0;
uint8_t process0SREG = 0;
void* process1StackPointer = 0;
uint8_t process1SREG = 0;
ISR(TIMER0_OCA_vect)
{
  currentThread = !currentThread;
  if (currentThread == 0)
  {
    process0StackPointer = StackPointer;  //Back up the stack pointer of the
    //"0" thread and the status register
    process0SREG = SREG;
    StackPointer = process1StackPointer 
  }
  else
  {
    process1StackPointer = StackPointer;  //Back up the stack pointer of the
    //"1" thread and the status register
    process1SREG = SREG;
    StackPointer = process0StackPointer 
  }

}

Is this a plausible thing to do, or is there something that I am not seeing here?

Best Answer

To create an adequate multi-threading system, you need to do a 'context switch' see Wikipedia 'context switch' for an explanation.

The code needs to make the 'context switch' be 'invisible' to each thread. Otherwise a thread will not be possible to restart reliably, destroying the value of doing it.

To make an invisible context switch to any running thread all of its state needs to be saved. It isn't sufficient to just save the SREG (status register), and flip to another stack. It needs to save all registers that could possibly be in use. Clearly the obvious set is the entire AVR register file, 32 registers. They would need to be saved, and the register file for the thread about to be resumed loaded in such a way that everything will be restored to the same state at the point that thread was previously interrupted.

Summary: yes an AVR multi-threaded system could be built using a timer, however, your code needs to save and restore a lot more state.

As I said in my comment, an AVR running at its highest speed of 20MHz, needs one or more cycles to complete one instruction. It would take about 32 instructions, and hence 64 cycles to store all of the 32 registers into memory, and about 64 cycles more to load the registers for a different thread, plus a little bit more for IRS entry and exit.

So, you should estimate about 140 cycles to create the most basic multi-threading context switch. That is 7µs at 20MHz clock. I'd suggest you keep the context switch well under 10% of the available CPU cycles so that it can actually get some useful work done. So have the interrupt less frequent than 70µs.

A possible place to help you understand the scope of the issues is to look at an existing simple OS. Then you will understand all of the other pieces which are likely to be needed more quickly. For example something like FreeRTOS.

This 'Arduino OS' forum page may help too.

A lot of the context switch code will need to be in assembler because you can't reliably get at the registers from C. However, because the AVR's instruction set is relatively simple, that shouldn't be too hard.

Each thread is usually represented by several different chunks of memory (RAM): 1. The register values 2. The stack

Atmel's AVR don't have a lot of RAM, so one awkward part is allocating enough stack for the thread, without using so much memory that there isn't room for enough threads.

The last little wrinkle that I remember is an exit from ISR (RETI) isn't exactly the same as a return from subroutine (RET), so you'll need to think that through too.