From efa9bb88a24db4e30350037749ed62b584e801d5 Mon Sep 17 00:00:00 2001 From: chudov Date: Sun, 1 Dec 2019 12:38:36 +0100 Subject: [PATCH] cpu/atmega256rfr2: symbol counter based RTT support --- cpu/atmega_common/include/periph_cpu_common.h | 26 ++- cpu/atmega_common/periph/rtt.c | 212 ++++++++++++++++-- 2 files changed, 219 insertions(+), 19 deletions(-) diff --git a/cpu/atmega_common/include/periph_cpu_common.h b/cpu/atmega_common/include/periph_cpu_common.h index 39ae69d8ca..25562e1582 100644 --- a/cpu/atmega_common/include/periph_cpu_common.h +++ b/cpu/atmega_common/include/periph_cpu_common.h @@ -177,15 +177,33 @@ typedef struct { /** @} */ /** -* @name RTT configuration -* @{ -*/ -#define RTT_MAX_VALUE (0x00FFFFFF) /* 24-bit timer */ + * @name RTT configuration + * @{ + */ +#if defined(SCCR0) && !defined(RTT_BACKEND_SC) +#define RTT_BACKEND_SC (1) +#endif +#if RTT_BACKEND_SC +/* For MCU with MAC symbol counter */ +#ifndef RTT_MAX_VALUE +#define RTT_MAX_VALUE (0xFFFFFFFFUL) /* 32-bit timer */ +#endif + +#ifndef RTT_FREQUENCY +#define RTT_FREQUENCY (62500UL) /* in Hz. */ +#endif + +#else +/* For MCU without MAC symbol counter */ +#ifndef RTT_MAX_VALUE +#define RTT_MAX_VALUE (0x00FFFFFF) /* 24-bit timer */ +#endif /* possible values: 32, 128, 256, 512, 1024, 4096, 32768 */ #ifndef RTT_FREQUENCY #define RTT_FREQUENCY (1024U) /* in Hz. */ #endif +#endif /** @} */ #ifdef __cplusplus diff --git a/cpu/atmega_common/periph/rtt.c b/cpu/atmega_common/periph/rtt.c index b76149118d..a35b7d591c 100644 --- a/cpu/atmega_common/periph/rtt.c +++ b/cpu/atmega_common/periph/rtt.c @@ -18,7 +18,9 @@ * oscillator. * * @author Matthew Blue + * @author Alexander Chudov * + * For all atmega except rfa1, rfr2 * In order to safely sleep when using the RTT: * 1. Disable interrupts * 2. Write to one of the asynch registers (e.g. TCCR2A) @@ -26,11 +28,23 @@ * 4. Re-enable interrupts * 5. Sleep before interrupt re-enable takes effect * + * For MCUs with a MAC symbol counter (ATmegaXXXRFA1 and ATmegaXXXRFR2): + * The MAC symbol counter is a 32 bit counter which can be sourced by a 62.5 kHz + * clock, derived from the 16 MHz system clock or from the 32.768 kHz RTC. + * When either the CPU or the transceivers is going to sleep, + * the MAC symbol counter is sourced by the RTC for both options. In order to + * not have to compensate for a changing clock frequency, this RTT + * implementation uses the 32.768kHz RTC as source even when both CPU and + * transceiver are active. The 32 bit comparator in SCOCR2 is used for alarms. + * + * SCCR0 is defined if an MCU has MAC symbol counter + * * @} */ #include +#include "byteorder.h" #include "cpu.h" #include "irq.h" #include "periph/rtt.h" @@ -39,8 +53,61 @@ #define ENABLE_DEBUG (0) #include "debug.h" +#if RTT_BACKEND_SC +/* + * Read a 32 bit register as described in section 10.3 of the datasheet: A read + * of the least significant byte causes the current value to be atomically + * captured in a temporary 32 bit registers. The remaining reads will access this + * register instead. Only a single 32 bit temporary register is used to provide + * means to atomically access them. Thus, interrupts must be disabled during the + * read sequence in order to prevent other threads (or ISRs) from updating the + * temporary 32 bit register before the reading sequence has completed. + */ +static inline uint32_t reg32_read(volatile uint8_t *reg_ll) +{ + le_uint32_t reg; + unsigned state = irq_disable(); + reg.u8[0] = reg_ll[0]; + reg.u8[1] = reg_ll[1]; + reg.u8[2] = reg_ll[2]; + reg.u8[3] = reg_ll[3]; + irq_restore(state); + return reg.u32; +} + +/* + * Write a 32 bit register done in the same manner as read: A write of the least + * significant byte causes the atomic store the 32 bit value in the registers + */ +static inline void reg32_write(volatile uint8_t *reg_ll, uint32_t _val) +{ + le_uint32_t val = { .u32 = _val }; + unsigned state = irq_disable(); + reg_ll[3] = val.u8[3]; + reg_ll[2] = val.u8[2]; + reg_ll[1] = val.u8[1]; + reg_ll[0] = val.u8[0]; + irq_restore(state); +} + +/* To build proper register names */ +#ifndef CONCAT +#define CONCAT(a, b) (a##b) +#endif + +/* To read the whole 32-bit register */ +#define RG_READ32(reg) (reg32_read(&CONCAT(reg, LL))) + +/* To write the whole 32-bit register */ +#define RG_WRITE32(reg, val) (reg32_write(&CONCAT(reg, LL), val)) + +/** @} */ +#endif + typedef struct { +#if RTT_BACKEND_SC == 0 uint16_t ext_comp; /* Extend compare to 24-bits */ +#endif rtt_cb_t alarm_cb; /* callback called from RTT alarm */ void *alarm_arg; /* argument passed to the callback */ rtt_cb_t overflow_cb; /* callback called when RTT overflows */ @@ -48,19 +115,29 @@ typedef struct { } rtt_state_t; static rtt_state_t rtt_state; +#if RTT_BACKEND_SC == 0 static uint16_t ext_cnt; +#endif static inline void _asynch_wait(void) { +#if RTT_BACKEND_SC + /* Wait until counter update flag clear. */ + while (SCSR & ((1 << SCBSY) )) {} +#else /* Wait until all busy flags clear. According to the datasheet, * this can take up to 2 positive edges of TOSC1 (32kHz). */ while (ASSR & ((1 << TCN2UB) | (1 << OCR2AUB) | (1 << OCR2BUB) | (1 << TCR2AUB) | (1 << TCR2BUB))) {} +#endif } /* interrupts are disabled here */ static uint32_t _safe_cnt_get(void) { +#if RTT_BACKEND_SC + return RG_READ32(SCCNT); +#else uint8_t cnt = TCNT2; /* If an overflow occurred since we disabled interrupts, manually @@ -80,8 +157,44 @@ static uint32_t _safe_cnt_get(void) } return (ext_cnt << 8) | cnt; +#endif } +#if RTT_BACKEND_SC +static inline void _timer_init(void) +{ + /* + * ATmega256RFR2 symbol counter init sequence: + * 1. Disable all related interrupts + * 2. Enable 32 kHz oscillator + * 3. Enable symbol counter, clock it from TOSC1 only + * 4. Reset prescaller, enable rx timestamping, start symbol counter + */ + + /* Disable all symbol counter interrupts */ + SCIRQM = 0; + + /* Clear all interrupt flags by writing '1' */ + SCIRQS = (1 << IRQSBO) | (1 << IRQSOF) + | (1 << IRQSCP3) | (1 << IRQSCP2) | (1 << IRQSCP1); + + /* Reset compare values */ + RG_WRITE32(SCOCR1, 0); + RG_WRITE32(SCOCR2, 0); + RG_WRITE32(SCOCR3, 0); + + /* Enable 32 kHz oscillator. All T/C2-related settings are overridden */ + ASSR = (1 << AS2); + + /* Enable symbol counter, clock from TOSC1, timestamping enabled */ + SCCR0 = (1 << SCRES) | (1 << SCEN) | (1 << SCCKSEL) | (1 << SCTSE); + + /* Reset the symbol counter */ + RG_WRITE32(SCCNT, 0); + /* Wait until not busy anymore */ + DEBUG("RTT waits until SC not busy\n"); +} +#else static inline uint8_t _rtt_div(uint16_t freq) { switch (freq) { @@ -96,11 +209,8 @@ static inline uint8_t _rtt_div(uint16_t freq) } } -void rtt_init(void) +static inline void _timer_init(void) { - DEBUG("Initializing RTT\n"); - - rtt_poweron(); /* * From the datasheet section "Asynchronous Operation of Timer/Counter2" @@ -134,15 +244,26 @@ void rtt_init(void) /* Wait until not busy anymore */ DEBUG("RTT waits until ASSR not busy\n"); +} +#endif + +void rtt_init(void) +{ + DEBUG("Initializing RTT\n"); + + rtt_poweron(); + + _timer_init(); _asynch_wait(); +#if RTT_BACKEND_SC == 0 /* Clear interrupt flags */ /* Oddly, this is done by writing ones; see datasheet */ TIFR2 = (1 << OCF2B) | (1 << OCF2A) | (1 << TOV2); /* Enable 8-bit overflow interrupt */ TIMSK2 |= (1 << TOIE2); - +#endif DEBUG("RTT initialized\n"); } @@ -172,12 +293,12 @@ uint32_t rtt_get_counter(void) { unsigned state; uint32_t now; - +#if RTT_BACKEND_SC == 0 /* Make sure it is safe to read TCNT2, in case we just woke up */ DEBUG("RTT sleeps until safe to read TCNT2\n"); TCCR2A = 0; _asynch_wait(); - +#endif state = irq_disable(); now = _safe_cnt_get(); irq_restore(state); @@ -188,12 +309,18 @@ uint32_t rtt_get_counter(void) void rtt_set_counter(uint32_t counter) { /* Wait until not busy anymore (should be immediate) */ - DEBUG("RTT sleeps until safe to write TCNT2\n"); + DEBUG("RTT sleeps until safe to write\n"); _asynch_wait(); /* Make non-atomic writes atomic (for concurrent access) */ unsigned state = irq_disable(); +#if RTT_BACKEND_SC + /* Clear overflow flag by writing a one; see datasheet */ + SCIRQS = (1 << IRQSOF); + RG_WRITE32(SCCNT, counter); + _asynch_wait(); +#else /* Prevent overflow flag from being set during update */ TCNT2 = 0; @@ -203,26 +330,44 @@ void rtt_set_counter(uint32_t counter) ext_cnt = (uint16_t)(counter >> 8); TCNT2 = (uint8_t)counter; - +#endif irq_restore(state); - - DEBUG("RTT set counter TCNT2: %" PRIu8 ", ext_cnt: %" PRIu16 "\n", - TCNT2, ext_cnt); } void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) { /* Disable alarm */ rtt_clear_alarm(); +#if RTT_BACKEND_SC + /* Make non-atomic writes atomic */ + unsigned state = irq_disable(); + /* Set the alarm value to SCOCR2. Atomic for concurrent access */ + RG_WRITE32(SCOCR2, alarm); + + rtt_state.alarm_cb = cb; + rtt_state.alarm_arg = arg; + + irq_restore(state); + + DEBUG("RTT set alarm SCCNT: %" PRIu32 ", SCOCR2: %" PRIu32 "\n", + RG_READ32(SCCNT), RG_READ32(SCOCR2)); + + /* Enable alarm interrupt */ + SCIRQS |= (1 << IRQSCP2); + SCIRQM |= (1 << IRQMCP2); + + DEBUG("RTT alarm interrupt active\n"); + +#else /* Make sure it is safe to read TCNT2, in case we just woke up, and */ /* safe to write OCR2B (in case it was busy) */ DEBUG("RTT sleeps until safe read TCNT2 and to write OCR2B\n"); TCCR2A = 0; _asynch_wait(); - /* Make non-atomic writes atomic */ unsigned state = irq_disable(); + uint32_t now = _safe_cnt_get(); /* Set the alarm value. Atomic for concurrent access */ @@ -249,11 +394,17 @@ void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) else { DEBUG("RTT alarm interrupt not active\n"); } + +#endif } uint32_t rtt_get_alarm(void) { +#if RTT_BACKEND_SC + return RG_READ32(SCOCR2); +#else return (rtt_state.ext_comp << 8) | OCR2A; +#endif } void rtt_clear_alarm(void) @@ -262,8 +413,11 @@ void rtt_clear_alarm(void) unsigned state = irq_disable(); /* Disable alarm interrupt */ +#if RTT_BACKEND_SC + SCIRQM &= ~(1 << IRQMCP2); +#else TIMSK2 &= ~(1 << OCIE2A); - +#endif rtt_state.alarm_cb = NULL; rtt_state.alarm_arg = NULL; @@ -272,14 +426,34 @@ void rtt_clear_alarm(void) void rtt_poweron(void) { +#if RTT_BACKEND_SC + SCCR0 |= (1 << SCEN); +#else power_timer2_enable(); +#endif } void rtt_poweroff(void) { +#if RTT_BACKEND_SC + SCCR0 &= ~(1 << SCEN); +#else power_timer2_disable(); +#endif } +#if RTT_BACKEND_SC +ISR(SCNT_OVFL_vect) +{ + atmega_enter_isr(); + /* Execute callback */ + if (rtt_state.overflow_cb != NULL) { + rtt_state.overflow_cb(rtt_state.overflow_arg); + } + + atmega_exit_isr(); +} +#else ISR(TIMER2_OVF_vect) { atmega_enter_isr(); @@ -305,13 +479,21 @@ ISR(TIMER2_OVF_vect) atmega_exit_isr(); } +#endif +#if RTT_BACKEND_SC +ISR(SCNT_CMP2_vect) +#else ISR(TIMER2_COMPA_vect) +#endif { atmega_enter_isr(); /* Disable alarm interrupt */ +#if RTT_BACKEND_SC + SCIRQM &= ~(1 << IRQMCP2); +#else TIMSK2 &= ~(1 << OCIE2A); - +#endif if (rtt_state.alarm_cb != NULL) { /* Clear callback */ rtt_cb_t cb = rtt_state.alarm_cb;