1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/pkg/openwsn/contrib/sctimer_rtt.c
2023-07-18 12:24:10 +02:00

210 lines
5.2 KiB
C

/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
* 2020 Inria
*
* 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 pkg_openwsn
* @{
*
* For details on the implementation check pkg/openwsn/doc.txt
*
* @file
* @brief RTT based adaptation of "sctimer" bsp module
*
* @author Tengfei Chang <tengfei.chang@gmail.com>, July 2012
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>, July 2017
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>, April 2019
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include <stdint.h>
#include <stdatomic.h>
#include "sctimer.h"
#include "debugpins.h"
#include "board.h"
#include "periph/rtt.h"
#define LOG_LEVEL LOG_NONE
#include "log.h"
/**
* @brief Maximum counter difference to not consider an ISR late, this
* should account for the largest timer interval OpenWSN
* scheduler might work with. When running only the stack this
* should not be more than SLOT_DURATION, but when using cjoin
* it is 65535ms
*/
#ifndef SCTIMER_LOOP_THRESHOLD
#define SCTIMER_LOOP_THRESHOLD (2 * PORT_TICS_PER_MS * 65535)
#endif
/* OpenWSN needs at least 32 tics per ms,use time division to reach that
if needed */
#ifdef RTT_FREQUENCY
#if RTT_FREQUENCY < 32768U
#define SCTIMER_TIME_DIVISION (1)
#if (SCTIMER_FREQUENCY % RTT_FREQUENCY) != 0
#error "RTT_FREQUENCY not supported"
#endif
#endif
#endif
#ifdef SCTIMER_TIME_DIVISION
#define SCTIMER_PRESCALER __builtin_ctz( \
SCTIMER_FREQUENCY / RTT_FREQUENCY)
#define SCTIMER_TIME_DIVISION_MASK (RTT_MAX_VALUE >> SCTIMER_PRESCALER)
#define SCTIMER_PRESCALER_MASK (~SCTIMER_TIME_DIVISION_MASK)
#define SCTIMER_PRESCALER_SHIFT __builtin_ctz(SCTIMER_TIME_DIVISION_MASK)
static uint32_t _prescaler;
static atomic_bool _enable;
#endif
#if RTT_MAX_VALUE < UINT32_MAX
/* If RTT_MAX_VALUE is smaller the UINT32_MAX then handle the remaining
bits here, sctimer is scheduled at least every slot (20ms) so no
overflow will be missed */
#define SCTIMER_RTT_EXTEND_MSB (1 << (32UL - __builtin_clz(RTT_MAX_VALUE)))
static atomic_uint_fast32_t _counter_msb;
static atomic_uint_fast32_t _now_last;
#endif
static sctimer_cbt sctimer_cb;
static void sctimer_isr_internal(void *arg)
{
(void)arg;
if (sctimer_cb != NULL) {
debugpins_isr_set();
sctimer_cb();
debugpins_isr_clr();
}
}
void sctimer_init(void)
{
rtt_init();
sctimer_cb = NULL;
#ifdef SCTIMER_RTT_EXTEND_MSB
atomic_store(&_counter_msb, 0);
atomic_store(&_now_last, 0);
#endif
#ifdef SCTIMER_TIME_DIVISION
_prescaler = 0;
_enable = false;
#endif
}
void sctimer_set_callback(sctimer_cbt cb)
{
sctimer_cb = cb;
}
#ifdef SCTIMER_TIME_DIVISION
uint32_t _update_val(uint32_t val, uint32_t now)
{
now = now & SCTIMER_PRESCALER_MASK;
val = val >> SCTIMER_PRESCALER;
/* Check if next value would cause an overflow */
if ((now - val) > SCTIMER_LOOP_THRESHOLD && _enable && now > val) {
_prescaler += (1 << SCTIMER_PRESCALER_SHIFT);
_enable = false;
}
/* Make sure it only updates the _prescaler once per overflow cycle */
if (val > SCTIMER_LOOP_THRESHOLD && val < 2 * SCTIMER_LOOP_THRESHOLD) {
_enable = true;
}
val |= _prescaler;
return val;
}
#endif
#ifdef SCTIMER_RTT_EXTEND_MSB
uint32_t _sctimer_extend(uint32_t now)
{
unsigned state = irq_disable();
uint32_t now_last = atomic_load(&_now_last);
uint32_t counter_msb = atomic_load(&_counter_msb);
if (now < now_last) {
/* account for overflow */
counter_msb += SCTIMER_RTT_EXTEND_MSB;
atomic_store(&_counter_msb, counter_msb);
}
atomic_store(&_now_last, now);
now += counter_msb;
irq_restore(state);
return now;
}
#endif
void sctimer_setCompare(uint32_t val)
{
unsigned state = irq_disable();
uint32_t now = rtt_get_counter();
#ifdef SCTIMER_RTT_EXTEND_MSB
now = _sctimer_extend(now);
#endif
#ifdef SCTIMER_TIME_DIVISION
val = _update_val(val, now);
#endif
if ((int32_t)now - val < SCTIMER_LOOP_THRESHOLD && now > val) {
rtt_set_alarm((now + RTT_MIN_OFFSET) & RTT_MAX_VALUE,
sctimer_isr_internal, NULL);
}
else {
if ((int32_t)val - now < RTT_MIN_OFFSET) {
rtt_set_alarm((now + RTT_MIN_OFFSET) & RTT_MAX_VALUE,
sctimer_isr_internal, NULL);
}
else {
rtt_set_alarm(val & RTT_MAX_VALUE, sctimer_isr_internal,
NULL);
}
}
irq_restore(state);
LOG_DEBUG("[sctimer]: set cb to %" PRIu32 " at %" PRIu32 "\n",
(uint32_t) val, now);
}
uint32_t sctimer_readCounter(void)
{
uint32_t now = rtt_get_counter();
#ifdef SCTIMER_RTT_EXTEND_MSB
now = _sctimer_extend(now);
#endif
#ifdef SCTIMER_TIME_DIVISION
now &= SCTIMER_TIME_DIVISION_MASK;
now = (now << SCTIMER_PRESCALER);
#endif
LOG_DEBUG("[sctimer]: now %" PRIu32 "\n", now);
return now;
}
void sctimer_enable(void)
{
rtt_poweron();
}
void sctimer_disable(void)
{
rtt_poweroff();
}