Skip to content

Append simple multithreading support #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cores/arduino/HardwareSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ size_t HardwareSerial::write(uint8_t c)
if(bit_is_set(*_ucsra, UDRE0))
_tx_udr_empty_irq();
} else {
// nop, the interrupt handler will free up space for us
// share CPU while buffer free waiting
yield();
}
}

Expand Down
20 changes: 14 additions & 6 deletions cores/arduino/wiring.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,22 @@ unsigned long micros() {

void delay(unsigned long ms)
{
uint32_t start = micros();
unsigned long new_tick;
unsigned long old_tick = millis();

while (ms > 0) {
while (ms)
{
// execute other threads
yield();
while ( ms > 0 && (micros() - start) >= 1000) {
ms--;
start += 1000;
}

// provides convergence
new_tick = millis();

if (new_tick - old_tick > ms) ms = 0;
else
ms -= new_tick - old_tick;

old_tick = new_tick;
}
}

Expand Down
674 changes: 674 additions & 0 deletions libraries/Thread/LICENSE

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions libraries/Thread/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Thread

It is simplest implementation of iterative multithredaing for AVR8 microcontrollers.
You can install this library by the library manager Arduino also.

Using
-----

See example for how to.

#### functions

`spawn(..)` create new thread with function and unique stack resources

`yield()` switch execution to next thread immediately

`hold()` disable yield() and time division functionality

`schedule()` enable yield() functionality only (switching points)

`quantize()` enable time division of the execution flows and yield()

`grab(..)` implement mutual exclusion barrier lock (semaphore)

`loose(.)` unlock barrier what has been locked by `grab(..)` call

#### constant

`MAIN` pointer to dummy stack of the main root thread

#### variables

`thread` dynamic pointer on the process stack structure
what can be used for reentrant and identification


257 changes: 257 additions & 0 deletions libraries/Thread/Thread.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/*****************************************************************************
* Simplest kernel for iterative multithtreading *
* Autor: Vyacheslav Azarov <[email protected]> *
* Licensed by GNU GPL V3 from 29 June 2007 *
* ***************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <setjmp.h>
#include <stddef.h>
#include <Arduino.h>
#include "Thread.h"

/* Definitions what depend on hardware */
#if __AVR_ARCH__ > 6
#error "Microcontroller architecture dooes not supported by this solution! "
#endif

#define QUANTUM_VECTOR TIMER0_COMPB_vect
#define QUANTUM_VALUE 250

#define __packed
#define __stackup __attribute__((optimize("conserve-stack")))
#define __thread __attribute__((OS_task))

#define disable_interrupts() cli()
#define enable_interrupts() sei()

#define barrier() __asm__ volatile("" ::: "memory")

#define set_stack_pointer(sp)\
\
barrier(); SP = (uint16_t)(sp);

#define enter_kernel_section()\
\
unsigned char __sreg__ = SREG;\
barrier(); SREG = 0;

#define leave_kernel_section()\
\
barrier(); SREG = __sreg__;

#define restart_quantum_timer()\
\
(OCR0B = TCNT0 + QUANTUM_VALUE)

#define disable_quantum_timer() (TIMSK0 &= ~_BV(OCIE0B))

#define enable_quantum_timer()\
\
restart_quantum_timer();\
TIMSK0 |= _BV(OCIE0B); TCCR0B &= ~_BV(COM0B1)


#ifdef __AVR_HAVE_RAMPZ__
#define __PUSH_RAMPZ__\
"\t in r24,__RAMPZ__" "\n"\
"\t push r24" "\n"

#define __POP_RAMPZ__\
"\t pop r24" "\n"\
"\t out __RAMPZ__,r24" "\n"
#else
#define __PUSH_RAMPZ__
#define __POP_RAMPZ__
#endif

#if defined(EIND)
#define __PUSH_EIND__\
"__EIND__ = 0x3C" "\n"\
"\t in r24,__EIND__" "\n"\
"\t push r24" "\n"

#define __POP_EIND__\
"\t pop r24" "\n"\
"\t out __EIND__,r24" "\n"
#else
#define __PUSH_EIND__
#define __POP_EIND__
#endif

__attribute__ ((signal, naked, used, externally_visible))
void QUANTUM_VECTOR (void)
{
__asm__ (
"\t push r0" "\n"
"\t in r0,__SREG__" "\n"
"\t push r0" "\n"
"\t push r1" "\n"
"\t clr __zero_reg__" "\n"
"\t push r18" "\n"
"\t push r19" "\n"
"\t push r20" "\n"
"\t push r21" "\n"
"\t push r22" "\n"
"\t push r23" "\n"
"\t push r24" "\n"
"\t push r25" "\n"
"\t push r26" "\n"
"\t push r27" "\n"
"\t push r30" "\n"
"\t push r31" "\n"
__PUSH_EIND__
__PUSH_RAMPZ__
"\t rcall .L03" "\n"
"\t cli" "\n"
);
yield();

__asm__ (
__POP_RAMPZ__
__POP_EIND__
"\t pop r31" "\n"
"\t pop r30" "\n"
"\t pop r27" "\n"
"\t pop r26" "\n"
"\t pop r25" "\n"
"\t pop r24" "\n"
"\t pop r23" "\n"
"\t pop r22" "\n"
"\t pop r21" "\n"
"\t pop r20" "\n"
"\t pop r19" "\n"
"\t pop r18" "\n"
"\t pop r1" "\n"
"\t pop r0" "\n"
"\t out __SREG__,r0" "\n"
"\t pop r0" "\n"
"\t sei" "\n"
"\t ret" "\n"
".L03:" "\n"
"\t reti" "\n"
);}


/* Hardware independent description */

typedef struct ring_t {
struct ring_t * next; // next record into ring of threads
jmp_buf * context; // the context stored in the local stack
void (*__thread loop)(void); // start function of the thread
}
__packed ring_t;


ring_t main_thread = {&main_thread,NULL,NULL};
const void * MAIN = &main_thread+1;
void * thread = &main_thread+1;

static bool yield_enable = false;

__stackup void spawn(void * state, void (*__thread start)(void))
{
jmp_buf context;
enter_kernel_section();

// store return
((ring_t*)thread-1)->context = &context;

// insert new thread
((ring_t*)state-1)->loop = start;
((ring_t*)state-1)->next = ((ring_t*)thread-1)->next;
((ring_t*)thread-1)->next = (ring_t*)state-1;
thread = state;

// switch thread
if (!setjmp(context))
{
set_stack_pointer((char*)((ring_t*)thread-1)-1);

// repeat always
for (;;)
{
((ring_t*)thread-1)->loop();
yield();
}
}

restart_quantum_timer();
leave_kernel_section();
enable_interrupts();
}

void hold(void)
{
yield_enable = false;
disable_quantum_timer();
}

void schedule(void)
{
yield_enable = true;
}

void quantize(void)
{
schedule();
enable_quantum_timer();
}

void yield(void)
{
jmp_buf context;

if (!yield_enable) return;
enter_kernel_section();

// change to next
((ring_t*)thread-1)->context = &context;
thread = ((ring_t*)thread-1)->next + 1;

// switch context
if (!setjmp(context))
longjmp(*((ring_t*)thread-1)->context, 0);

restart_quantum_timer();
leave_kernel_section();
}

bool grab(void ** barrier, unsigned long timeout)
{
unsigned long starttime;
enter_kernel_section();

starttime = millis();

while (*barrier != NULL && *barrier != thread)
{
yield();
if (millis() - starttime > timeout)
{
leave_kernel_section();
return false;
}
}
*barrier = thread;

leave_kernel_section();
return true;
}

bool loose(void ** barrier)
{
enter_kernel_section();

if (*barrier == thread || *barrier == NULL)
{
*barrier = NULL;
leave_kernel_section();
return true;
}

leave_kernel_section();
return false;
}


44 changes: 44 additions & 0 deletions libraries/Thread/Thread.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*****************************************************************************
* Simplest kernel for iterative multithtreading *
* Autor: Vyacheslav Azarov <[email protected]> *
* Licensed by GNU GPL V3 from 29 June 2007 *
* ***************************************************************************/
#ifndef _THREAD_H_
#define _THREAD_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdbool.h>

extern const void * MAIN; // dummy state pointer of main thread
extern void * thread; // pointer to current thread state

void spawn(void * state, void (*start)(void)); // creation new thread

// switching control

void hold(void); // disable thread switching and quantize
void schedule(void); // enable thread swithing by yield only
void quantize(void); // enable 1 ms quantizing and yield

// explicit swithing

void yield(void); // immediately switchng to next thread

// mutual exclussion

bool grab(void ** barrier, unsigned long timeout);
// returns true if the barrier is blocked or false if the
// timeout has expired

bool loose(void ** barrier);
// returns true if barrier unblocked

#ifdef __cplusplus
}
#endif
#endif

Loading