1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00

atmega: use software interrupt for context swap

Fixes #5745
For AVR based boards, three defines must be defined AVR_CONTEXT_SWAP_INIT,
AVR_CONTEXT_SWAP_INTERRUPT_VECT, and AVR_CONTEXT_SWAP_TRIGGER.
These defines are used to trigger a software interrupt used for context
switching.

When AVR_CONTEXT_SWAP_INTERRUPT_VECT is handled, the scheduler is run
and a context swap will happen if necessary, with the resulting thread
starting following the reti instruction. This results in threads running
at normal priority instead of at interrupt priority.

Atmega devices do provide a pure software interrupt. The method used
here for waspmote-pro and arduino-mega2560 is to use pin change
interrupts, set the pin to act as an output, and toggle the value to
simulate a software interrupt. The main limitation here is that a
physical pin is now occupied and must be defined for each board
supported by RIOT. On the plus side, it provides an easy method for
detecting context swaps with an oscilloscope.
This commit is contained in:
Jon Thacker 2016-08-20 08:24:03 -05:00
parent 3f82b530ad
commit e0365e0bf9
3 changed files with 65 additions and 10 deletions

View File

@ -52,6 +52,22 @@ extern "C" {
#define LED0_TOGGLE (PORTB ^= LED0_MASK)
/** @} */
/**
* Context swap defines
* Setup to use PJ6 which is pin change interrupt 15 (PCINT15)
* This emulates a software triggered interrupt
**/
#define AVR_CONTEXT_SWAP_INIT do { \
DDRJ |= (1 << PJ6); \
PCICR |= (1 << PCIE1); \
PCMSK1 |= (1 << PCINT15); \
} while (0)
#define AVR_CONTEXT_SWAP_INTERRUPT_VECT PCINT1_vect
#define AVR_CONTEXT_SWAP_TRIGGER PORTJ ^= (1 << PJ6)
/**
* @brief xtimer configuration values
* @{

View File

@ -150,6 +150,19 @@ extern "C" {
/** @} */
/**
* Context swap defines
* Setup to use PB5 which is pin change interrupt 5
* This emulates a software triggered interrupt
**/
#define AVR_CONTEXT_SWAP_INIT do { \
DDRB |= (1 << PB5); \
PCICR |= (1 << PCIE0); \
PCMSK0 |= (1 << PCINT5); \
} while (0)
#define AVR_CONTEXT_SWAP_INTERRUPT_VECT PCINT0_vect
#define AVR_CONTEXT_SWAP_TRIGGER PORTB ^= (1 << PB5)
/**
* @brief xtimer configuration values
* @{

View File

@ -25,11 +25,37 @@
#include "sched.h"
#include "irq.h"
#include "cpu.h"
#include "board.h"
/**
* @brief AVR_CONTEXT_SWAP_INIT intialize the context swap trigger
* Called when threading is first started.
*/
#ifndef AVR_CONTEXT_SWAP_INIT
#error AVR_CONTEXT_SWAP_INIT must be defined in board.h
#endif
/**
* @brief AVR_CONTEXT_SWAP_INTERRUPT_VECT Name of the ISR to use for context swapping
*/
#ifndef AVR_CONTEXT_SWAP_INTERRUPT_VECT
#error AVR_CONTEXT_SWAP_INTERRUPT_VECT must be defined in board.h
#endif
/**
* @brief AVR_CONTEXT_SWAP_TRIGGER executed to start the context swap
* When executed, this should result in the interrupt named in
* AVR_CONTEXT_SWAP_INTERRUPT_VECT being called
*/
#ifndef AVR_CONTEXT_SWAP_TRIGGER
#error ARV_CONTEXT_SWAP_TRIGGER must be defined in board.h
#endif
/*
* local function declarations (prefixed with __)
*/
static void __context_save(void);
static void __context_restore(void);
static void __enter_thread_mode(void);
@ -122,7 +148,6 @@ char *thread_arch_stack_init(thread_task_func_t task_func, void *arg,
stk--;
*stk = (uint8_t) 0x00;
#endif
#if defined(RAMPZ)
stk--;
*stk = (uint8_t) 0x00;
@ -221,6 +246,7 @@ void thread_arch_start_threading(void) __attribute__((naked));
void thread_arch_start_threading(void)
{
sched_run();
AVR_CONTEXT_SWAP_INIT;
__enter_thread_mode();
}
@ -238,17 +264,17 @@ void NORETURN __enter_thread_mode(void)
UNREACHABLE();
}
void thread_arch_yield(void) __attribute__((naked));
void thread_arch_yield(void)
{
void thread_arch_yield(void) {
AVR_CONTEXT_SWAP_TRIGGER;
}
// Use this interrupt to perform all context switches
ISR(AVR_CONTEXT_SWAP_INTERRUPT_VECT, ISR_NAKED) {
__context_save();
/* irq_disable(); */ /* gets already disabled during __context_save() */
sched_run();
irq_enable();
__context_restore();
__asm__ volatile("ret");
__asm__ volatile("reti");
}