In this short post, I want to present a small project on which I have been working for the last couple of weeks. It is a small library to control an AVR micro-controller execution context. Additionally, it can be used on AVR-based Arduino boards.

I have to note that this is my first serious attempt in micro-controllers programming. Nevertheless, I tried to do my best, but bugs are always possible, so bug reports are welcome. Caveat lector.

Initially, the project started as a side effect of my attempt to understand how task switching is performed in operating systems. I believe that it is better to use microcontrollers to understand the concepts like this one because the generic computers are meant to run full-featured, multi-user operating systems with virtual memory support. These things, while important on their own, considerably complicate the low-level programming to the point where it is hard to see the forest behind the trees.

The functionality, developed in the process, turned out to be generic enough, so I decided to continue to work on the project, and implemented substitutes for functions getcontext(), setcontext(), makecontext(), swapcontext(). These functions are commonly available on UNIX-like operating systems because they used to be a part of the POSIX standard (You can read about them here and here). They can be used to implement many interesting abstractions, but in most cases, this functionality is used to implement coroutines. I decided to go ahead and implemented the asymmetric stackful coroutines (as in the Lua programming language).

I tried to make the library's interface as easy to use as possible. There are 8 functions (4 to work with execution context, 4 for to work with coroutines), 4 macros and 2 data types. The interface can be summarised as follows (to give you the high level view):

/* Context Switching: avrcontext.h */

typedef struct avr_context_t_ {
    /* ... */
} avr_context_t;

typedef void (*avr_context_func_t)(void *);

#define AVR_SAVE_CONTEXT(presave_code, load_address_to_Z_code) /* ... */
#define AVR_RESTORE_CONTEXT(load_address_to_Z_code) /* ... */

#define AVR_SAVE_CONTEXT_GLOBAL_POINTER(presave_code, global_context_pointer) /* ... */
#define AVR_RESTORE_CONTEXT_GLOBAL_POINTER(global_context_pointer) /* ... */

void avr_getcontext(avr_context_t *cp);
void avr_setcontext(const avr_context_t *cp);

void avr_swapcontext(avr_context_t *oucp, const avr_context_t *cp);
void avr_makecontext(avr_context_t *cp,
                     void *stackp, const size_t stack_size,
                     const avr_context_t *successor_cp,
                     avr_context_func_t funcp, void *funcargp);

/* Asymmetric Stackful Coroutines: avrcoro.h */

typedef struct avr_coro_t_ {
    /* ... */
} avr_coro_t;

typedef enum avr_coro_state_t_ {
    AVR_CORO_SUSPENDED = 0,
    AVR_CORO_RUNNING,
    AVR_CORO_DEAD,
    AVR_CORO_ILLEGAL,
} avr_coro_state_t;

typedef void *(*avr_coro_func_t)(avr_coro_t *, void *);

int avr_coro_init(avr_coro_t *coro,
                  void *stackp, const size_t stack_size,
                  avr_coro_func_t func);
int avr_coro_resume(avr_coro_t *coro, void **data);
int avr_coro_yield(avr_coro_t *self, void **data);
avr_coro_state_t avr_coro_state(const avr_coro_t *coro);

I do not want to repeat myself here, so if you are interested, you can find the full project documentation on the project's page on GitHub. There are examples included. I tried to arrange them in such a way that one can use them as a tutorial. They are, I hope, properly documented as well.

The list of available examples:

The library can be installed via "Library Manager" in Arduino IDE (search for AVR-context).

libmgr.png

The project turned out to be very interesting on itself and involved some research. The references to the specifications, papers and other resources can be found in the project's documentation. I may do a couple of publications which demonstrate how this library was developed. Although the result is hardware specific, the approach itself can be adapted to almost any hardware platform.