From 54ac292c14f9fbde304f952a06afded49238f7db Mon Sep 17 00:00:00 2001 From: Matthew Blue Date: Sat, 7 Apr 2018 15:20:31 -0400 Subject: [PATCH 1/5] cpu/atmega_common: make internal pin handling sharable --- cpu/atmega_common/include/periph_cpu_pins.h | 47 +++++++++++++++++ cpu/atmega_common/periph/gpio.c | 56 --------------------- 2 files changed, 47 insertions(+), 56 deletions(-) create mode 100644 cpu/atmega_common/include/periph_cpu_pins.h diff --git a/cpu/atmega_common/include/periph_cpu_pins.h b/cpu/atmega_common/include/periph_cpu_pins.h new file mode 100644 index 000000000000..d55b0f3a77f3 --- /dev/null +++ b/cpu/atmega_common/include/periph_cpu_pins.h @@ -0,0 +1,47 @@ +#define GPIO_BASE_PORT_A (0x20) +#define GPIO_OFFSET_PORT_H (0xCB) +#define GPIO_OFFSET_PIN_PORT (0x02) +#define GPIO_OFFSET_PIN_PIN (0x03) + +/** + * @brief Extract the pin number of the given pin + */ +static inline uint8_t _pin_num(gpio_t pin) +{ + return (pin & 0x0f); +} + +/** + * @brief Extract the port number of the given pin + */ +static inline uint8_t _port_num(gpio_t pin) +{ + return (pin >> 4) & 0x0f; +} + +/** + * @brief Generate the PORTx address of the give pin. + */ +static inline uint16_t _port_addr(gpio_t pin) +{ + uint8_t port_num = _port_num(pin); + uint16_t port_addr = port_num * GPIO_OFFSET_PIN_PIN; + + port_addr += GPIO_BASE_PORT_A; + port_addr += GPIO_OFFSET_PIN_PORT; + +#if defined (PORTG) + if (port_num > PORT_G) { + port_addr += GPIO_OFFSET_PORT_H; + } +#endif + return port_addr; +} + +/** + * @brief Generate the DDRx address of the given pin + */ +static inline uint16_t _ddr_addr(gpio_t pin) +{ + return (_port_addr(pin) - 0x01); +} diff --git a/cpu/atmega_common/periph/gpio.c b/cpu/atmega_common/periph/gpio.c index 4d61c08e5867..41e4b8178490 100644 --- a/cpu/atmega_common/periph/gpio.c +++ b/cpu/atmega_common/periph/gpio.c @@ -31,11 +31,6 @@ #include "periph/gpio.h" #include "periph_conf.h" -#define GPIO_BASE_PORT_A (0x20) -#define GPIO_OFFSET_PORT_H (0xCB) -#define GPIO_OFFSET_PIN_PORT (0x02) -#define GPIO_OFFSET_PIN_PIN (0x03) - /* * @brief Define GPIO interruptions for an specific atmega CPU, by default * 2 (for small atmega CPUs) @@ -58,57 +53,6 @@ static gpio_isr_ctx_t config[GPIO_EXT_INT_NUMOF]; -/** - * @brief Extract the pin number of the given pin - */ -static inline uint8_t _pin_num(gpio_t pin) -{ - return (pin & 0x0f); -} - -/** - * @brief Extract the port number of the given pin - */ -static inline uint8_t _port_num(gpio_t pin) -{ - return (pin >> 4) & 0x0f; -} - -/** - * @brief Generate the PORTx address of the give pin. - */ -static inline uint16_t _port_addr(gpio_t pin) -{ - uint8_t port_num = _port_num(pin); - uint16_t port_addr = port_num * GPIO_OFFSET_PIN_PIN; - - port_addr += GPIO_BASE_PORT_A; - port_addr += GPIO_OFFSET_PIN_PORT; - -#if defined (PORTG) - if (port_num > PORT_G) { - port_addr += GPIO_OFFSET_PORT_H; - } -#endif - return port_addr; -} - -/** - * @brief Generate the DDRx address of the given pin - */ -static inline uint16_t _ddr_addr(gpio_t pin) -{ - return (_port_addr(pin) - 0x01); -} - -/** - * @brief Generate the PINx address of the given pin. - */ -static inline uint16_t _pin_addr(gpio_t pin) -{ - return (_port_addr(pin) - 0x02); -} - int gpio_init(gpio_t pin, gpio_mode_t mode) { switch (mode) { From 89ba27a6377c2d2f602b4319d4abe3ba72fa4039 Mon Sep 17 00:00:00 2001 From: Matthew Blue Date: Sat, 7 Apr 2018 15:23:02 -0400 Subject: [PATCH 2/5] fixup! cpu/atmega_common: make internal pin handling sharable --- cpu/atmega_common/periph/gpio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cpu/atmega_common/periph/gpio.c b/cpu/atmega_common/periph/gpio.c index 41e4b8178490..9a8a7ce9d4c4 100644 --- a/cpu/atmega_common/periph/gpio.c +++ b/cpu/atmega_common/periph/gpio.c @@ -30,6 +30,7 @@ #include "cpu.h" #include "periph/gpio.h" #include "periph_conf.h" +#include "periph_cpu_pins.h" /* * @brief Define GPIO interruptions for an specific atmega CPU, by default From 4aa47d0667ac1c5a733b31588be71d536044bc8f Mon Sep 17 00:00:00 2001 From: Matthew Blue Date: Sat, 7 Apr 2018 18:07:32 -0400 Subject: [PATCH 3/5] fixup! cpu/atmega_common: make internal pin handling sharable --- cpu/atmega_common/include/periph_cpu_pins.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cpu/atmega_common/include/periph_cpu_pins.h b/cpu/atmega_common/include/periph_cpu_pins.h index d55b0f3a77f3..d0f961046e0b 100644 --- a/cpu/atmega_common/include/periph_cpu_pins.h +++ b/cpu/atmega_common/include/periph_cpu_pins.h @@ -45,3 +45,15 @@ static inline uint16_t _ddr_addr(gpio_t pin) { return (_port_addr(pin) - 0x01); } + +static inline uint8_t _pcicr_num(gpio_t pin) +{ +} + +static inline uint16_t _pcmsk_addr(gpio_t pin) +{ +} + +static inline uint8_t _pcint_num(gpio_t pin) +{ +} From d1b79d91875462eb66ddee525e5ce4be150a3723 Mon Sep 17 00:00:00 2001 From: Matthew Blue Date: Sat, 7 Apr 2018 18:07:48 -0400 Subject: [PATCH 4/5] cpu/atmega_common: context switching rewrite --- cpu/atmega_common/thread_arch.c | 214 +++++++++++++++++++++++++++++--- 1 file changed, 199 insertions(+), 15 deletions(-) diff --git a/cpu/atmega_common/thread_arch.c b/cpu/atmega_common/thread_arch.c index c5833e14ea75..aa07de24d5c1 100644 --- a/cpu/atmega_common/thread_arch.c +++ b/cpu/atmega_common/thread_arch.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * 2018 Matthew Blue * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -14,6 +15,7 @@ * @brief Implementation of the kernel's architecture dependent thread interface * * @author Hinnerk van Bruinehsen + * @author Matthew Blue * * @} */ @@ -25,31 +27,49 @@ #include "irq.h" #include "cpu.h" #include "board.h" +#include "periph_cpu_pins.h" /** - * @brief AVR_CONTEXT_SWAP_INIT initialize the context swap trigger - * Called when threading is first started. + * @brief Boards must define one: AVR_CONTEXT_SWAP_PIN or AVR_CONTEXT_SWAP_CNTR + * AVR_CONTEXT_SWAP_PIN is used to trigger a PCINT which switches the context + * it may be defined as any unused pin that has a PCINT. + * AVR_CONTEXT_SWAP_CNTR is used to trigger an OVF which switches the context + * it may be defined as the number of an unused timer/counter. */ -#ifndef AVR_CONTEXT_SWAP_INIT -#error AVR_CONTEXT_SWAP_INIT must be defined in board.h +#if !(defined AVR_CONTEXT_SWAP_PIN) && !(defined AVR_CONTEXT_SWAP_CNTR) +#error AVR_CONTEXT_SWAP_PIN or AVR_CONTEXT_SWAP_CNTR must be defined in board.h +#endif +#if defined AVR_CONTEXT_SWAP_PIN && defined AVR_CONTEXT_SWAP_CNTR +#error AVR_CONTEXT_SWAP_PIN and AVR_CONTEXT_SWAP_CNTR are conflicting defines #endif /** - * @brief AVR_CONTEXT_SWAP_INTERRUPT_VECT Name of the ISR to use for context swapping + * @brief If using pin context swaps, AVR_CONTEXT_SWAP_INTERRUPT_VECT must also + * be defined. */ -#ifndef AVR_CONTEXT_SWAP_INTERRUPT_VECT -#error AVR_CONTEXT_SWAP_INTERRUPT_VECT must be defined in board.h +#if defined AVR_CONTEXT_SWAP_PIN && !(defined AVR_CONTEXT_SWAP_INTERRUPT_VECT) +#error AVR_CONTEXT_SWAP_INTERRUPT_VECT must be defined with AVR_CONTEXT_SWAP_PIN #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 + * @brief Defines for AVR_CONTEXT_SWAP_INTERRUPT_VECT when using a counter. */ -#ifndef AVR_CONTEXT_SWAP_TRIGGER -#error ARV_CONTEXT_SWAP_TRIGGER must be defined in board.h -#endif +#ifdef AVR_CONTEXT_SWAP_CNTR +#if AVR_CONTEXT_SWAP_CNTR == 0 +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT TIMER0_OVF_vect +#elif AVR_CONTEXT_SWAP_CNTR == 1 +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT TIMER1_OVF_vect +#elif AVR_CONTEXT_SWAP_CNTR == 2 +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT TIMER2_OVF_vect +#elif AVR_CONTEXT_SWAP_CNTR == 3 +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT TIMER3_OVF_vect +#elif AVR_CONTEXT_SWAP_CNTR == 4 +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT TIMER4_OVF_vect +#elif AVR_CONTEXT_SWAP_CNTR == 5 +#define AVR_CONTEXT_SWAP_INTERRUPT_VECT TIMER5_OVF_vect +#endif /* AVR_CONTEXT_SWAP_CNTR == # */ +#endif /* ifdef AVR_CONTEXT_SWAP_CNTR */ /* @@ -226,7 +246,92 @@ void cpu_switch_context_exit(void) __attribute__((naked)); void cpu_switch_context_exit(void) { sched_run(); - AVR_CONTEXT_SWAP_INIT; + +/* Context swap pin option */ +#ifdef AVR_CONTEXT_SWAP_PIN + /* Set pin mode low (ready to trigger */ + _SFR_MEM8(_port_addr(AVR_CONTEXT_SWAP_PIN)) &= + ~(1 << _pin_num(AVR_CONTEXT_SWAP_PIN)); + + /* Set data direction to output */ + _SFR_MEM8(_ddr_addr(AVR_CONTEXT_SWAP_PIN)) |= + (1 << _pin_num(AVR_CONTEXT_SWAP_PIN)); + + /* Clear interrupt flags on pin */ + PCIFR &= ~(1 << _pcicr_num(AVR_CONTEXT_SWAP_PIN)); + + /* Enable interrupts on port */ + PCICR |= (1 << _pcicr_num(AVR_CONTEXT_SWAP_PIN)); + + /* Enable interrupt for pin */ + _SFR_MEM8(_pcmsk_addr(AVR_CONTEXT_SWAP_PIN)) |= + (1 << _pcint_num(AVR_CONTEXT_SWAP_PIN)); +#endif /* AVR_CONTEXT_SWAP_PIN */ + +/* Context swap counter option */ +#ifdef AVR_CONTEXT_SWAP_CNTR + /* Counter based context swap initialization procedure: + * - Set CTC mode (TCCR0A & TCCR0B) + * (so it can never loop around and overflow again without reset) + * - Set prescaler to no clock source (TCCR0B) + * - Set max value if not reset (OCR0A) + * - Enable overflow interrupt (TIMSK0) + * - Clear overflow flags (TIFR0) + * - Set counter to just short of overflowing (TCNT0) + */ +#if AVR_CONTEXT_SWAP_CNTR == 0 + TCCR0A = (1 << WGM01); + TCCR0B = 0; + OCR0A = 0x80; + TIMSK0 = (1 << TOIE0); + TIFR0 = 0; + TCNT0 = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 1 + TCCR1A = 0; + TCCR1B = (1 << WGM12); + OCR1AH = 0x80; + OCR1AL = 0; + TIMSK1 = (1 << TOIE1); + TIFR1 = 0; + TCNT1H = 0xFF; + TCNT1L = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 2 + TCCR2A = (1 << WGM21); + TCCR2B = 0; + OCR2A = 0x80; + TIMSK2 = (1 << TOIE2); + TIFR2 = 0; + TCNT2 = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 3 + TCCR3A = 0; + TCCR3B = (1 << WGM32); + OCR3AH = 0x80; + OCR3AL = 0; + TIMSK3 = (1 << TOIE3); + TIFR3 = 0; + TCNT3H = 0xFF; + TCNT3L = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 4 + TCCR4A = 0; + TCCR4B = (1 << WGM42); + OCR4AH = 0x80; + OCR4AL = 0; + TIMSK4 = (1 << TOIE4); + TIFR4 = 0; + TCNT4H = 0xFF; + TCNT4L = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 5 + TCCR5A = 0; + TCCR5B = (1 << WGM52); + OCR5AH = 0x80; + OCR5AL = 0; + TIMSK5 = (1 << TOIE5); + TIFR5 = 0; + TCNT5H = 0xFF; + TCNT5L = 0xFF; +#endif /* AVR_CONTEXT_SWAP_CNTR == # */ +#endif /* ifdef AVR_CONTEXT_SWAP_CNTR */ + __enter_thread_mode(); } @@ -245,12 +350,91 @@ void NORETURN __enter_thread_mode(void) } void thread_yield_higher(void) { - AVR_CONTEXT_SWAP_TRIGGER; +/* Context swap pin option */ +#ifdef AVR_CONTEXT_SWAP_PIN + /* Trigger ISR by setting pin high */ + _SFR_MEM8(_port_addr(AVR_CONTEXT_SWAP_PIN)) |= + (1 << _pin_num(AVR_CONTEXT_SWAP_PIN)); + + /* Wait for ISR to set pin low to prevent race condition */ + while (_port_addr(AVR_CONTEXT_SWAP_PIN) & + (1 << _pin_num(AVR_CONTEXT_SWAP_PIN))); +#endif /* AVR_CONTEXT_SWAP_PIN */ + +/* Context swap counter option */ +#ifdef AVR_CONTEXT_SWAP_CNTR + /* Trigger ISR by enabling counter clock */ + /* Then wait for ISR to set counter to prevent race condition */ +#if AVR_CONTEXT_SWAP_CNTR == 0 + TCCR0B |= (1 << CS00); + while (TCNT0 != 0xFF); +#elif AVR_CONTEXT_SWAP_CNTR == 1 + TCCR1B |= (1 << CS10); + while (TCNT1H != 0xFF); +#elif AVR_CONTEXT_SWAP_CNTR == 2 + TCCR2B |= (1 << CS20); + while (TCNT2 != 0xFF); +#elif AVR_CONTEXT_SWAP_CNTR == 3 + TCCR3B |= (1 << CS30); + while (TCNT1H != 0xFF); +#elif AVR_CONTEXT_SWAP_CNTR == 4 + TCCR4B |= (1 << CS40); + while (TCNT1H != 0xFF); +#elif AVR_CONTEXT_SWAP_CNTR == 5 + TCCR5B |= (1 << CS50); + while (TCNT1H != 0xFF); +#endif /* AVR_CONTEXT_SWAP_CNTR == # */ +#endif /* ifdef AVR_CONTEXT_SWAP_CNTR */ } /* Use this interrupt to perform all context switches */ ISR(AVR_CONTEXT_SWAP_INTERRUPT_VECT, ISR_NAKED) { +/* Context swap pin option */ +#ifdef AVR_CONTEXT_SWAP_PIN + /* Disable interrupt to prevent second interrupt flag */ + PCICR &= ~(1 << _pcicr_num(AVR_CONTEXT_SWAP_PIN)); + + /* Set pin low to signal ISR grab */ + _SFR_MEM8(_port_addr(AVR_CONTEXT_SWAP_PIN)) &= + ~(1 << _pin_num(AVR_CONTEXT_SWAP_PIN)); + + /* Wait for pin low to complete (to prevent second interrupt race) */ + while (~(_port_addr(AVR_CONTEXT_SWAP_PIN)) & + (1 << _pin_num(AVR_CONTEXT_SWAP_PIN))); + + /* Reenable interrupt */ + PCICR |= (1 << _pcicr_num(AVR_CONTEXT_SWAP_PIN)); +#endif /* AVR_CONTEXT_SWAP_PIN */ + +/* Context swap counter option */ +#ifdef AVR_CONTEXT_SWAP_CNTR + /* Disable counter clock and reset count */ +#if AVR_CONTEXT_SWAP_CNTR == 0 + TCCR0B &= ~(1 << CS00); + TCNT0 = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 1 + TCCR1B &= ~(1 << CS10); + TCNT1L = 0xFF; + TCNT1H = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 2 + TCCR2B &= ~(1 << CS20); + TCNT2 = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 3 + TCCR3B &= ~(1 << CS30); + TCNT3L = 0xFF; + TCNT3H = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 4 + TCCR4B &= ~(1 << CS40); + TCNT4L = 0xFF; + TCNT4H = 0xFF; +#elif AVR_CONTEXT_SWAP_CNTR == 5 + TCCR5B &= ~(1 << CS50); + TCNT5L = 0xFF; + TCNT5H = 0xFF; +#endif /* AVR_CONTEXT_SWAP_CNTR == # */ +#endif /* ifdef AVR_CONTEXT_SWAP_CNTR */ + __context_save(); sched_run(); __context_restore(); From 9205feecc2787cb97b9e13bca00a4dbc8236a3b7 Mon Sep 17 00:00:00 2001 From: Matthew Blue Date: Sat, 7 Apr 2018 21:45:26 -0400 Subject: [PATCH 5/5] fixup! cpu/atmega_common: make internal pin handling sharable --- cpu/atmega_common/include/periph_cpu_pins.h | 71 +++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/cpu/atmega_common/include/periph_cpu_pins.h b/cpu/atmega_common/include/periph_cpu_pins.h index d0f961046e0b..79ccffe68717 100644 --- a/cpu/atmega_common/include/periph_cpu_pins.h +++ b/cpu/atmega_common/include/periph_cpu_pins.h @@ -1,3 +1,28 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * 2016 INRIA + * 2018 Matthew Blue + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega_common + * @{ + * + * @file + * @brief CPU specific definitions for internal pin handling + * + * @author René Herthel + * @author Francisco Acosta + * @author Laurent Navet + * @author Matthew Blue + * + * @} + */ + #define GPIO_BASE_PORT_A (0x20) #define GPIO_OFFSET_PORT_H (0xCB) #define GPIO_OFFSET_PIN_PORT (0x02) @@ -46,14 +71,60 @@ static inline uint16_t _ddr_addr(gpio_t pin) return (_port_addr(pin) - 0x01); } +/** + * @brief Extract the PCIEx number of the given pin (for PCICR reg) + */ static inline uint8_t _pcicr_num(gpio_t pin) { + switch (_port_num(pin)) { +#if defined(CPU_ATMEGA1281) || defined(CPU_ATMEGA2560) + case PORT_B: return PCIE0; + case PORT_E: return PCIE1; + case PORT_J: return PCIE1; + case PORT_K: return PCIE2; +#elif defined(CPU_ATMEGA1284P) + case PORT_A: return PCIE0; + case PORT_B: return PCIE1; + case PORT_C: return PCIE2; + case PORT_D: return PCIE3; +#elif defined(CPU_ATMEGA256RFR2) + case PORT_B: return PCIE0; + case PORT_E: return PCIE1; +#elif defined(CPU_ATMEGA328P) + case PORT_B: return PCIE0; + case PORT_C: return PCIE1; + case PORT_D: return PCIE2; +#endif + } } +/** + * @brief Generate the PCMSKx address of the given pin + */ static inline uint16_t _pcmsk_addr(gpio_t pin) { + switch (_pcicr_num(pin)) { + case PCIE0: return PCMSK0; + case PCIE1: return PCMSK1; + case PCIE2: return PCMSK2; +#ifdef PCMSK3 + case PCIE3: return PCMSK3; +#endif + } } +/** + * @brief Extract the PCINTxx number of the given pin (for PCMSKx reg) + */ static inline uint8_t _pcint_num(gpio_t pin) { +#if defined(CPU_ATMEGA1281) || defined(CPU_ATMEGA2560) + if (_port_num(pin) == PORT_J) { + return _pin_num(pin) + 1; + } else { + return _pin_num(pin); + } +#else + return _pin_num(pin); +#endif }