From 3ff44f77b9a15181caae1ce01450c5bdc9f97781 Mon Sep 17 00:00:00 2001 From: Simon Duquennoy Date: Wed, 2 Dec 2015 10:25:32 +0100 Subject: [PATCH 1/2] jn516x: added sleep modes, support for 32kHz rtimer, and tickless clock --- platform/jn516x/Makefile.jn516x | 12 +- platform/jn516x/README.md | 30 ++- platform/jn516x/contiki-jn516x-main.c | 176 ++++++++++++--- platform/jn516x/dev/clock.c | 288 +++++++++++++++---------- platform/jn516x/dev/micromac-radio.c | 87 +++++--- platform/jn516x/dev/rtimer-arch-slow.c | 183 ++++++++++++++++ platform/jn516x/dev/rtimer-arch.c | 86 +++++--- platform/jn516x/dev/rtimer-arch.h | 70 +++++- platform/jn516x/dev/uart-driver.h | 2 + platform/jn516x/platform-conf.h | 76 +++++-- 10 files changed, 769 insertions(+), 241 deletions(-) create mode 100644 platform/jn516x/dev/rtimer-arch-slow.c diff --git a/platform/jn516x/Makefile.jn516x b/platform/jn516x/Makefile.jn516x index 50f0a393d..c7d0353d5 100644 --- a/platform/jn516x/Makefile.jn516x +++ b/platform/jn516x/Makefile.jn516x @@ -9,6 +9,11 @@ ifdef CHIP else JENNIC_CHIP ?= JN5168 endif +ifdef MODULE + JN5168_MODULE = $(MODULE) +else + JN5168_MODULE ?= M00 +endif JENNIC_PCB ?= DEVKIT4 JENNIC_STACK ?= MAC JENNIC_MAC ?= MiniMac @@ -75,7 +80,8 @@ SIZE:=$(CROSS_COMPILE)-size OBJCOPY:=$(CROSS_COMPILE)-objcopy OBJDUMP:=$(CROSS_COMPILE)-objdump -ARCH = ccm-star.c exceptions.c rtimer-arch.c slip_uart0.c clock.c micromac-radio.c \ +ARCH = ccm-star.c exceptions.c rtimer-arch.c rtimer-arch-slow.c \ + slip_uart0.c clock.c micromac-radio.c \ mtarch.c node-id.c watchdog.c log.c ringbufindex.c slip.c sprintf.c # Default uart0 for printf and slip TARGET_WITH_UART0 ?= 1 @@ -126,6 +132,10 @@ CONTIKI_TARGET_DIRS += dev/dongle ARCH += leds-arch.c endif +ifeq ($(JENNIC_CHIP),JN5168) +CFLAGS += -DJN5168_$(JN5168_MODULE) +endif + ifdef nodemac CFLAGS += -DMACID=$(nodemac) endif diff --git a/platform/jn516x/README.md b/platform/jn516x/README.md index d67bfd029..b593895aa 100644 --- a/platform/jn516x/README.md +++ b/platform/jn516x/README.md @@ -44,10 +44,14 @@ The following features have been implemented: * A radio driver with two modes (polling and interrupt based) * CCM* driver with HW accelerated AES * UART driver (with HW and SW flow control, 1'000'000 baudrate by default) - * Contiki system clock and rtimers (16MHz tick frequency based on 32 MHz crystal) - * 32.768kHz external oscillator + * Contiki tickless clock + * Contiki rtimers based on either + * the 32 kHz external oscillator + * or the internal 32 MHz oscillator (which gives a 16 MHz rtimer) + * CPU low-power mdoes + * doze mode: shallow sleep, 32 MHz oscillator (source of rtimer and radio clock) keeps running + * sleep mode: deeper sleep, 32 MHz oscillator turned off, next wakeup set on 32 kHz oscillator * Periodic DCO recalibration - * CPU "doze" mode * HW random number generator used as a random seed for pseudo-random generator * Watchdog, JN516x HW exception handlers @@ -60,7 +64,6 @@ The following hardware platforms have been tested: ## TODO list The following features are planned: - * CPU deeper sleep mode support (where the 32 MHz crystal is turned off) * Time-accurate radio primitives ("send at", "listen until") * External storage @@ -157,12 +160,31 @@ The following MCU models are supported: Set `CHIP` variable to change this; for example, to select JN5164 use: `make CHIP=JN5164` +The JN5168 has four module variants available: +* `M00` - Standard power, integrated antenna (default module) +* `M03` - Standard power, uFL connector +* `M05` - Medium power, uFL connector +* `M06` - High power, uFL connector + +The `M05` and `M06` need to control the internal power amplifier. Set the `MODULE` variable to select the module, for example: +`make CHIP=JN5168 MODULE=M05` + The following platform-specific configurations are supported: * DR1174 evaluation kit; enable this with `JN516x_WITH_DR1174 = 1` in your makefile * DR1174 with DR1175 sensor board; enable this with `JN516x_WITH_DR1175 = 1` (will set `JN516x_WITH_DR1174` automatically) * DR1174 with DR1199 sensor board; enable this with `JN516x_WITH_DR1199 = 1` (will set `JN516x_WITH_DR1174` automatically) * USB dongle; enable this with `JN516x_WITH_DONGLE = 1` +### Enabling specific hardware features + +The JN516X Contiki platform supports sleep mode (with RAM retention and keeping the external oscillator on). To enable sleeping, configure `JN516X_SLEEP_CONF_ENABLED=1`. + +Sleeping will only happen if there at least 50 ms until the next rtimer or etimer. Also, the system will wake up ~10 ms before the next timer should fire in order to reinitialize all hardware peripherals. + +The JN516X Contiki platform also supports rtimers at two different speeds: 16 MHz and 32 kHz. By default, the high-speed timer is used. The two timers have similar expected accuracy (drift ppm), but the 16 MHz one has higher precision. However, the low-speed timers are also kept running during sleeping. + +To enable the low-frequency timer option, set `RTIMER_USE_32KHZ=1`. An external crystal oscillator is required to achieve reasonable accuracy in this case. This oscilator is present on most platforms, and is enabled automatically if either 32kHz timers or sleeping are enabled. + ### Node IEEE/RIME/IPv6 Addresses Nodes will autoconfigure their IPv6 address based on their 64-bit IEEE/MAC address. The 64-bit MAC address is read directly from JN516x System on Chip. diff --git a/platform/jn516x/contiki-jn516x-main.c b/platform/jn516x/contiki-jn516x-main.c index d7b463b12..3192b0ee5 100644 --- a/platform/jn516x/contiki-jn516x-main.c +++ b/platform/jn516x/contiki-jn516x-main.c @@ -37,6 +37,7 @@ * * \author * Beshr Al Nahas + * Atis Elsts */ #include @@ -48,6 +49,7 @@ #include #include #include "dev/uart0.h" +#include "dev/uart-driver.h" #include "contiki.h" #include "net/netstack.h" @@ -113,6 +115,17 @@ static uint8_t is_gateway; #include "experiment-setup.h" #endif +/* _EXTRA_LPM is the sleep mode, _LPM is the doze mode */ +#define ENERGEST_TYPE_EXTRA_LPM ENERGEST_TYPE_LPM + +static void main_loop(void); + +#if DCOSYNCH_CONF_ENABLED +static unsigned long last_dco_calibration_time; +#endif +static uint64_t sleep_start; +static uint32_t sleep_start_ticks; + /*---------------------------------------------------------------------------*/ #define DEBUG 1 #if DEBUG @@ -255,25 +268,17 @@ set_linkaddr(void) #endif } /*---------------------------------------------------------------------------*/ -#if USE_EXTERNAL_OSCILLATOR -static bool_t -init_xosc(void) +bool_t +xosc_init(void) { /* The internal 32kHz RC oscillator is used by default; * Initialize and enable the external 32.768kHz crystal. */ vAHI_Init32KhzXtal(); - /* wait for 1.0 seconds for the crystal to stabilize */ - clock_time_t start = clock_time(); - clock_time_t now; - do { - now = clock_time(); - watchdog_periodic(); - } while(now - start < CLOCK_SECOND); - /* switch to the 32.768 kHz crystal */ + /* Switch to the 32.768kHz crystal. + * This will block and wait up to 1 sec for it to stabilize. */ return bAHI_Set32KhzClockMode(E_AHI_XTAL); } -#endif /*---------------------------------------------------------------------------*/ #if WITH_TINYOS_AUTO_IDS uint16_t TOS_NODE_ID = 0x1234; /* non-zero */ @@ -285,7 +290,23 @@ main(void) /* Set stack overflow address for detecting overflow in runtime */ vAHI_SetStackOverflow(TRUE, ((uint32_t *)&heap_location)[0]); + /* Initialize random with a seed from the SoC random generator. + * This must be done before selecting the high-precision external oscillator. + */ + vAHI_StartRandomNumberGenerator(E_AHI_RND_SINGLE_SHOT, E_AHI_INTS_DISABLED); + random_init(u16AHI_ReadRandomNumber()); + clock_init(); + rtimer_init(); + +#if JN516X_EXTERNAL_CRYSTAL_OSCILLATOR + /* initialize the 32kHz crystal and wait for ready */ + xosc_init(); + /* need to reinitialize because the wait-for-ready process uses system timers */ + clock_init(); + rtimer_init(); +#endif + watchdog_init(); leds_init(); leds_on(LEDS_ALL); @@ -308,20 +329,10 @@ main(void) } #endif - /* Initialize random with a seed from the SoC random generator. - * This must be done before selecting the high-precision external oscillator. - */ - vAHI_StartRandomNumberGenerator(E_AHI_RND_SINGLE_SHOT, E_AHI_INTS_DISABLED); - random_init(u16AHI_ReadRandomNumber()); - process_init(); ctimer_init(); uart0_init(UART_BAUD_RATE); /* Must come before first PRINTF */ -#if USE_EXTERNAL_OSCILLATOR - init_xosc(); -#endif - #if NETSTACK_CONF_WITH_IPV4 slip_arch_init(UART_BAUD_RATE); #endif /* NETSTACK_CONF_WITH_IPV4 */ @@ -403,10 +414,27 @@ main(void) #if NETSTACK_CONF_WITH_IPV6 start_uip6(); #endif /* NETSTACK_CONF_WITH_IPV6 */ + + /* need this to reliably generate the first rtimer callback and callbacks in other + auto-start processes */ + (void)u32AHI_Init(); + start_autostart_processes(); leds_off(LEDS_ALL); + + main_loop(); + + return -1; +} + +static void +main_loop(void) +{ int r; + clock_time_t time_to_etimer; + rtimer_clock_t ticks_to_rtimer; + while(1) { do { /* Reset watchdog. */ @@ -423,9 +451,8 @@ main(void) * if we have more than 500uSec until next rtimer * PS: Calibration disables interrupts and blocks for 200uSec. * */ - static unsigned long last_dco_calibration_time = 0; if(clock_seconds() - last_dco_calibration_time > DCOSYNCH_PERIOD) { - if(rtimer_arch_get_time_until_next_wakeup() > RTIMER_SECOND / 2000) { + if(rtimer_arch_time_to_rtimer() > RTIMER_SECOND / 2000) { /* PRINTF("ContikiMain: Calibrating the DCO\n"); */ eAHI_AttemptCalibration(); /* Patch to allow CpuDoze after calibration */ @@ -434,15 +461,51 @@ main(void) } } #endif /* DCOSYNCH_CONF_ENABLED */ - ENERGEST_OFF(ENERGEST_TYPE_CPU); - ENERGEST_ON(ENERGEST_TYPE_LPM); - vAHI_CpuDoze(); - watchdog_start(); - ENERGEST_OFF(ENERGEST_TYPE_LPM); - ENERGEST_ON(ENERGEST_TYPE_CPU); - } - return 0; + /* flush standard output before sleeping */ + uart_driver_flush(E_AHI_UART_0); + + /* calculate the time to the next etimer and rtimer */ + time_to_etimer = clock_arch_time_to_etimer(); + ticks_to_rtimer = rtimer_arch_time_to_rtimer(); + +#if JN516X_SLEEP_ENABLED + /* we can sleep only up to the next rtimer/etimer */ + rtimer_clock_t max_sleep_time = ticks_to_rtimer; + if(max_sleep_time >= JN516X_MIN_SLEEP_TIME) { + /* also take into account etimers */ + uint64_t ticks_to_etimer = ((uint64_t)time_to_etimer * RTIMER_SECOND) / CLOCK_SECOND; + max_sleep_time = MIN(ticks_to_etimer, ticks_to_rtimer); + } + + if(max_sleep_time >= JN516X_MIN_SLEEP_TIME) { + max_sleep_time -= JN516X_SLEEP_GUARD_TIME; + /* bound the sleep time to 1 second */ + max_sleep_time = MIN(max_sleep_time, JN516X_MAX_SLEEP_TIME); + +#if !RTIMER_USE_32KHZ + /* convert to 32.768 kHz oscillator ticks */ + max_sleep_time = (uint64_t)max_sleep_time * JN516X_XOSC_SECOND / RTIMER_SECOND; +#endif + vAHI_WakeTimerEnable(WAKEUP_TIMER, TRUE); + /* sync with the tick timer */ + WAIT_FOR_EDGE(sleep_start); + sleep_start_ticks = u32AHI_TickTimerRead(); + + vAHI_WakeTimerStartLarge(WAKEUP_TIMER, max_sleep_time); + ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_EXTRA_LPM); + vAHI_Sleep(E_AHI_SLEEP_OSCON_RAMON); + } else { +#else + { +#endif /* JN516X_SLEEP_ENABLED */ + clock_arch_schedule_interrupt(time_to_etimer, ticks_to_rtimer); + ENERGEST_SWITCH(ENERGEST_TYPE_CPU, ENERGEST_TYPE_LPM); + vAHI_CpuDoze(); + watchdog_start(); + ENERGEST_SWITCH(ENERGEST_TYPE_LPM, ENERGEST_TYPE_CPU); + } + } } /*---------------------------------------------------------------------------*/ void @@ -456,7 +519,52 @@ void AppWarmStart(void) { /* Wakeup after sleep with memory on. - * TODO: Need to initialize devices but not the application state */ - main(); + * Need to initialize devices but not the application state. + * Note: the actual time this function is called is + * ~8 ticks (32kHz timer) later than the scheduled sleep end time. + */ + uint32_t sleep_ticks; + uint64_t sleep_end; + rtimer_clock_t sleep_ticks_rtimer; + + clock_arch_calibrate(); + leds_init(); + uart0_init(UART_BAUD_RATE); /* Must come before first PRINTF */ + NETSTACK_RADIO.init(); + watchdog_init(); + watchdog_stop(); + + WAIT_FOR_EDGE(sleep_end); + sleep_ticks = (uint32_t)(sleep_start - sleep_end) + 1; + +#if RTIMER_USE_32KHZ + sleep_ticks_rtimer = sleep_ticks; +#else + { + static uint32_t remainder; + uint64_t t = (uint64_t)sleep_ticks * RTIMER_SECOND + remainder; + sleep_ticks_rtimer = (uint32_t)(t / JN516X_XOSC_SECOND); + remainder = t - sleep_ticks_rtimer * JN516X_XOSC_SECOND; + } +#endif + + /* reinitialize rtimers */ + rtimer_arch_reinit(sleep_start_ticks, sleep_ticks_rtimer); + + ENERGEST_SWITCH(ENERGEST_TYPE_EXTRA_LPM, ENERGEST_TYPE_CPU); + + watchdog_start(); + + /* reinitialize clock */ + clock_arch_init(1); + /* schedule etimer interrupt */ + clock_arch_schedule_interrupt(clock_arch_time_to_etimer(), rtimer_arch_time_to_rtimer()); + +#if DCOSYNCH_CONF_ENABLED + /* The radio is recalibrated on wakeup */ + last_dco_calibration_time = clock_seconds(); +#endif + + main_loop(); } /*---------------------------------------------------------------------------*/ diff --git a/platform/jn516x/dev/clock.c b/platform/jn516x/dev/clock.c index e2b1d46a3..b1927f5d6 100644 --- a/platform/jn516x/dev/clock.c +++ b/platform/jn516x/dev/clock.c @@ -32,9 +32,10 @@ /** * \file - * Clock implementation for NXP jn516x. + * Tickless clock implementation for NXP jn516x. * \author * Beshr Al Nahas + * Atis Elsts * */ @@ -47,10 +48,6 @@ #include "rtimer-arch.h" #include "dev/watchdog.h" -/** - * TickTimer will be used for RTIMER - * E_AHI_TIMER_1 will be used for ticking - **/ #define DEBUG 0 #if DEBUG @@ -60,84 +57,95 @@ #define PRINTF(...) #endif -static volatile unsigned long seconds = 0; -static volatile uint8_t ticking = 0; -static volatile clock_time_t clock_ticks = 0; -/* last_tar is used for calculating clock_fine */ - #define CLOCK_TIMER E_AHI_TIMER_1 -#define CLOCK_TIMER_ISR_DEV E_AHI_DEVICE_TIMER1 -/* 16Mhz / 2^7 = 125Khz */ -#define CLOCK_PRESCALE 7 -/* 10ms tick --> overflow after ~981/2 days */ -#define CLOCK_INTERVAL (125 * 10) -#define MAX_TICKS (CLOCK_INTERVAL) +#define CLOCK_TIMER_ISR_DEV E_AHI_DEVICE_TIMER1 + +#define OVERFLOW_TIMER E_AHI_TIMER_0 +#define OVERFLOW_TIMER_ISR_DEV E_AHI_DEVICE_TIMER0 + +/* 16Mhz / 2^10 = 15.625 kHz */ +#define CLOCK_PRESCALE 10 +#define PRESCALED_TICKS_PER_SECOND 15625 +/* 8ms tick --> overflow after ~397.7 days */ +#define CLOCK_INTERVAL 125 +/* Max schedulable number of ticks. + * Must not be more than: + * 0xffff / (16'000'000 / (1 << CLOCK_PRESCALE) / CLOCK_SECOND) + */ +#define CLOCK_MAX_SCHEDULABLE_TICKS 520 +/* Min guard time an etimer can be scheduled before an rtimer */ +#define CLOCK_RTIMER_GUARD_TIME US_TO_RTIMERTICKS(16) +/* Clock tick expressed as rtimer ticks */ +#define CLOCK_TICK ((1 << CLOCK_PRESCALE) * CLOCK_INTERVAL) + +#define RTIMER_OVERFLOW_PRESCALED 4194304 /* = 0x100000000 / (2^CLOCK_PRESCALE) */ +#define RTIMER_OVERFLOW_REMAINDER 54 /* in prescaled ticks, per one overflow */ + + +#define CLOCK_LT(a, b) ((int32_t)((a)-(b)) < 0) + +/*---------------------------------------------------------------------------*/ +static uint32_t +clock(void) +{ + /* same as rtimer_arch_now() */ + return u32AHI_TickTimerRead(); +} +/*---------------------------------------------------------------------------*/ +static uint32_t +check_rtimer_overflow(rtimer_clock_t now) +{ + static rtimer_clock_t last_rtimer_ticks; + static uint32_t clock_ticks_remainder; + static uint32_t clock_ticks_base; + + if(last_rtimer_ticks > now) { + clock_ticks_base += RTIMER_OVERFLOW_PRESCALED / CLOCK_INTERVAL; + clock_ticks_remainder += RTIMER_OVERFLOW_REMAINDER; + if(clock_ticks_remainder > CLOCK_INTERVAL) { + clock_ticks_remainder -= CLOCK_INTERVAL; + clock_ticks_base += 1; + } + } + last_rtimer_ticks = now; + return clock_ticks_base; +} +/*---------------------------------------------------------------------------*/ +static void +check_etimers(void) +{ + if(etimer_pending()) { + clock_time_t now = clock_time(); + if(!CLOCK_LT(now, etimer_next_expiration_time())) { + etimer_request_poll(); + } + } + process_nevents(); +} /*---------------------------------------------------------------------------*/ void clockTimerISR(uint32 u32Device, uint32 u32ItemBitmap) { - if(u32Device != CLOCK_TIMER_ISR_DEV) { + if(u32Device != CLOCK_TIMER_ISR_DEV && u32Device != OVERFLOW_TIMER_ISR_DEV) { return; } + ENERGEST_ON(ENERGEST_TYPE_IRQ); - watchdog_start(); - - clock_ticks++; - if(clock_ticks % CLOCK_CONF_SECOND == 0) { - ++seconds; - energest_flush(); - } - if(etimer_pending() && (etimer_next_expiration_time() - clock_ticks - 1) > MAX_TICKS) { - etimer_request_poll(); - /* TODO exit low-power mode */ - } - if(process_nevents() >= 0) { - /* TODO exit low-power mode */ + if(u32Device == CLOCK_TIMER_ISR_DEV) { + check_etimers(); } - watchdog_stop(); + if(u32Device == OVERFLOW_TIMER_ISR_DEV) { + check_rtimer_overflow(clock()); + } ENERGEST_OFF(ENERGEST_TYPE_IRQ); } /*---------------------------------------------------------------------------*/ -static void -clock_timer_init(void) -{ - vAHI_TimerEnable(CLOCK_TIMER, CLOCK_PRESCALE, 0, 1, 0); - vAHI_TimerClockSelect(CLOCK_TIMER, 0, 0); - - vAHI_TimerConfigureOutputs(CLOCK_TIMER, 0, 1); - vAHI_TimerDIOControl(CLOCK_TIMER, 0); - -#if (CLOCK_TIMER == E_AHI_TIMER_0) - vAHI_Timer0RegisterCallback(clockTimerISR); -#elif (CLOCK_TIMER == E_AHI_TIMER_1) - vAHI_Timer1RegisterCallback(clockTimerISR); -#endif - clock_ticks = 0; - vAHI_TimerStartRepeat(CLOCK_TIMER, 0, CLOCK_INTERVAL); - ticking = 1; -} -/*---------------------------------------------------------------------------*/ void -clock_init(void) +clock_arch_calibrate(void) { - /* gMAC_u8MaxBuffers = 2; */ -#ifdef JENNIC_CHIP_FAMILY_JN516x - /* Turn off debugger */ - *(volatile uint32 *)0x020000a0 = 0; -#endif - /* system controller interrupts callback is disabled - * -- Only wake Interrupts -- - */ - vAHI_SysCtrlRegisterCallback(0); - - /* schedule clock tick interrupt */ - clock_timer_init(); - rtimer_init(); - (void)u32AHI_Init(); - bAHI_SetClockRate(E_AHI_XTAL_32MHZ); /* Wait for oscillator to stabilise */ @@ -151,27 +159,55 @@ clock_init(void) | REG_SYSCTRL_PWRCTRL_SPIMEN_MASK); } /*---------------------------------------------------------------------------*/ -clock_time_t -clock_time(void) +void +clock_arch_init(int is_reinitialization) { - clock_time_t t1, t2; - do { - t1 = clock_ticks; - t2 = clock_ticks; - } while(t1 != t2); - return t1; + /* initialize etimer interrupt timer */ + vAHI_TimerEnable(CLOCK_TIMER, CLOCK_PRESCALE, 0, 1, 0); + vAHI_TimerClockSelect(CLOCK_TIMER, 0, 0); + + vAHI_TimerConfigureOutputs(CLOCK_TIMER, 0, 1); + vAHI_TimerDIOControl(CLOCK_TIMER, 0); + + vAHI_Timer1RegisterCallback(clockTimerISR); + + /* initialize and start rtimer overflow timer */ + vAHI_TimerEnable(OVERFLOW_TIMER, CLOCK_PRESCALE, 0, 1, 0); + vAHI_TimerClockSelect(OVERFLOW_TIMER, 0, 0); + + vAHI_TimerConfigureOutputs(OVERFLOW_TIMER, 0, 1); + vAHI_TimerDIOControl(OVERFLOW_TIMER, 0); + + vAHI_Timer0RegisterCallback(clockTimerISR); + vAHI_TimerStartRepeat(OVERFLOW_TIMER, 0, PRESCALED_TICKS_PER_SECOND * 4); + + if(is_reinitialization) { + /* check if the etimer has overflowed (useful when this is executed after sleep */ + check_rtimer_overflow(clock()); + } } /*---------------------------------------------------------------------------*/ void -clock_set(clock_time_t clock, clock_time_t fclock) +clock_init(void) { - clock_ticks = clock; + /* gMAC_u8MaxBuffers = 2; */ +#ifdef JENNIC_CHIP_FAMILY_JN516x + /* Turn off debugger */ + *(volatile uint32 *)0x020000a0 = 0; +#endif + + clock_arch_calibrate(); + + /* setup clock mode and interrupt handler */ + clock_arch_init(0); } /*---------------------------------------------------------------------------*/ -int -clock_fine_max(void) +clock_time_t +clock_time(void) { - return CLOCK_INTERVAL; + uint32_t now = clock(); + clock_time_t base = check_rtimer_overflow(now); + return base + now / CLOCK_TICK; } /*---------------------------------------------------------------------------*/ /** @@ -180,33 +216,19 @@ clock_fine_max(void) void clock_delay_usec(uint16_t dt) { - volatile uint32_t t = u32AHI_TickTimerRead(); -#define RTIMER_MAX_TICKS 0xffffffff - /* beware of wrapping */ - if(RTIMER_MAX_TICKS - t < dt) { - while(u32AHI_TickTimerRead() < RTIMER_MAX_TICKS && u32AHI_TickTimerRead() != 0) ; - dt -= RTIMER_MAX_TICKS - t; - t = 0; - } - while(u32AHI_TickTimerRead() - t < dt) { - watchdog_periodic(); - } + uint32_t end = clock() + dt; + /* Note: this does not call watchdog periodic() */ + while(CLOCK_LT(clock(), end)); } /*---------------------------------------------------------------------------*/ /** * Delay the CPU for a multiple of 8 us. */ void -clock_delay(unsigned int i) +clock_delay(unsigned int dt) { - volatile uint32_t t = u16AHI_TimerReadCount(CLOCK_TIMER); - /* beware of wrapping */ - if(MAX_TICKS - t < i) { - while(u16AHI_TimerReadCount(CLOCK_TIMER) < MAX_TICKS && u16AHI_TimerReadCount(CLOCK_TIMER) != 0) ; - i -= MAX_TICKS - t; - t = 0; - } - while(u16AHI_TimerReadCount(CLOCK_TIMER) - t < i) { + uint32_t end = clock() + dt * 128; + while(CLOCK_LT(clock(), end)) { watchdog_periodic(); } } @@ -218,33 +240,63 @@ clock_delay(unsigned int i) void clock_wait(clock_time_t t) { - clock_time_t start; - - start = clock_time(); - while(clock_time() - start < (clock_time_t)t) { + clock_time_t end = clock_time() + t; + while(CLOCK_LT(clock_time(), end)) { watchdog_periodic(); } } /*---------------------------------------------------------------------------*/ -void -clock_set_seconds(unsigned long sec) -{ - seconds = sec; -} -/*---------------------------------------------------------------------------*/ unsigned long clock_seconds(void) { - unsigned long t1, t2; - do { - t1 = seconds; - t2 = seconds; - } while(t1 != t2); - return t1; + return clock_time() / CLOCK_SECOND; } /*---------------------------------------------------------------------------*/ -rtimer_clock_t -clock_counter(void) +clock_time_t +clock_arch_time_to_etimer(void) { - return rtimer_arch_now(); + clock_time_t time_to_etimer; + if(etimer_pending()) { + time_to_etimer = etimer_next_expiration_time() - clock_time(); + if(time_to_etimer < 0) { + time_to_etimer = 0; + } + } else { + /* no active etimers */ + time_to_etimer = (clock_time_t)-1; + } + return time_to_etimer; } +/*---------------------------------------------------------------------------*/ +void +clock_arch_schedule_interrupt(clock_time_t time_to_etimer, rtimer_clock_t ticks_to_rtimer) +{ + if(time_to_etimer > CLOCK_MAX_SCHEDULABLE_TICKS) { + time_to_etimer = CLOCK_MAX_SCHEDULABLE_TICKS; + } + + time_to_etimer *= CLOCK_INTERVAL; + + if(ticks_to_rtimer != (rtimer_clock_t)-1) { + /* if the next rtimer is close enough to the etimer... */ + rtimer_clock_t ticks_to_etimer = time_to_etimer * (1 << CLOCK_PRESCALE); + +#if RTIMER_USE_32KHZ + ticks_to_rtimer = (uint64_t)ticks_to_rtimer * (F_CPU / 2) / RTIMER_SECOND; +#endif + + if(!CLOCK_LT(ticks_to_rtimer, ticks_to_etimer) + && CLOCK_LT(ticks_to_rtimer, ticks_to_etimer + CLOCK_RTIMER_GUARD_TIME)) { + /* ..then schedule the etimer after the rtimer */ + time_to_etimer += 2; + } + } + + /* interrupt will not be generated if 0 is passed as the parameter */ + if(time_to_etimer == 0) { + time_to_etimer = 1; + } + + vAHI_TimerStartSingleShot(CLOCK_TIMER, 0, time_to_etimer); +} +/*---------------------------------------------------------------------------*/ diff --git a/platform/jn516x/dev/micromac-radio.c b/platform/jn516x/dev/micromac-radio.c index 7c4f6132b..556019e5c 100644 --- a/platform/jn516x/dev/micromac-radio.c +++ b/platform/jn516x/dev/micromac-radio.c @@ -119,8 +119,6 @@ void vMMAC_SetChannelAndPower(uint8 u8Channel, int8 i8power); #define MICROMAC_CONF_AUTOACK 1 #endif /* MICROMAC_CONF_AUTOACK */ -#define RADIO_TO_RTIMER(X) ((rtimer_clock_t)((X) << (int32_t)8L)) - /* Set radio always on for now because this is what Contiki MAC layers * expect. */ #ifndef MICROMAC_CONF_ALWAYS_ON @@ -151,11 +149,11 @@ static uint8_t autoack_enabled = MICROMAC_CONF_AUTOACK; static uint8_t send_on_cca = 0; /* Current radio channel */ -static int current_channel; +static int current_channel = MICROMAC_CONF_CHANNEL; /* Current set point tx power Actual tx power may be different. Use get_txpower() for actual power */ -static int current_tx_power; +static int current_tx_power = MICROMAC_CONF_TX_POWER; /* an integer between 0 and 255, used only with cca() */ static uint8_t cca_thershold = MICROMAC_CONF_CCA_THR; @@ -278,44 +276,22 @@ frame802154_has_panid(frame802154_fcf_t *fcf, int *has_src_pan_id, int *has_dest static rtimer_clock_t get_packet_timestamp(void) { + /* Wait for an edge */ + uint32_t t = u32MMAC_GetTime(); + while(u32MMAC_GetTime() == t); /* Save SFD timestamp, converted from radio timer to RTIMER */ last_packet_timestamp = RTIMER_NOW() - - RADIO_TO_RTIMER((uint32_t)(u32MMAC_GetTime() - u32MMAC_GetRxTime())); + RADIO_TO_RTIMER((uint32_t)(u32MMAC_GetTime() - (u32MMAC_GetRxTime() - 1))); + /* The remaining measured error is typically in range 0..16 usec. + * Center it around zero, in the -8..+8 usec range. */ + last_packet_timestamp -= US_TO_RTIMERTICKS(8); return last_packet_timestamp; } /*---------------------------------------------------------------------------*/ static int -init(void) +init_software(void) { int put_index; - tsExtAddr node_long_address; - uint16_t node_short_address; - - tx_in_progress = 0; - - u32JPT_Init(); - vMMAC_Enable(); - - /* Enable/disable interrupts */ - if(poll_mode) { - vMMAC_EnableInterrupts(NULL); - vMMAC_ConfigureInterruptSources(0); - } else { - vMMAC_EnableInterrupts(&radio_interrupt_handler); - } vMMAC_ConfigureRadio(); - set_channel(MICROMAC_CONF_CHANNEL); - set_txpower(MICROMAC_CONF_TX_POWER); - - vMMAC_GetMacAddress(&node_long_address); - - /* Short addresses are disabled by default */ - node_short_address = (uint16_t)node_long_address.u32L; - vMMAC_SetRxAddress(frame802154_get_pan_id(), node_short_address, &node_long_address); - - /* Disable hardware backoff */ - vMMAC_SetTxParameters(1, 0, 0, 0); - vMMAC_SetCutOffTimer(0, FALSE); - /* Initialize ring buffer and first input packet pointer */ ringbufindex_init(&input_ringbuf, MIRCOMAC_CONF_BUF_NUM); /* get pointer to next input slot */ @@ -332,6 +308,42 @@ init(void) process_start(µmac_radio_process, NULL); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +init(void) +{ + int ret = 1; + tsExtAddr node_long_address; + uint16_t node_short_address; + static uint8_t is_initialized; + + tx_in_progress = 0; + + u32JPT_Init(); + vMMAC_Enable(); + + /* Enable/disable interrupts */ + if(poll_mode) { + vMMAC_EnableInterrupts(NULL); + vMMAC_ConfigureInterruptSources(0); + } else { + vMMAC_EnableInterrupts(&radio_interrupt_handler); + } + vMMAC_ConfigureRadio(); + set_channel(current_channel); + set_txpower(current_tx_power); + + vMMAC_GetMacAddress(&node_long_address); + /* Short addresses are disabled by default */ + node_short_address = (uint16_t)node_long_address.u32L; + vMMAC_SetRxAddress(frame802154_get_pan_id(), node_short_address, &node_long_address); + + /* Disable hardware backoff */ + vMMAC_SetTxParameters(1, 0, 0, 0); + vMMAC_SetCutOffTimer(0, FALSE); + #if RADIO_TEST_MODE == RADIO_TEST_MODE_HIGH_PWR /* Enable high power mode. * In this mode DIO2 goes high during RX @@ -348,7 +360,12 @@ init(void) u32REG_SysRead(REG_SYS_PWR_CTRL) | (1UL << 26UL)); #endif /* TEST_MODE */ - return 1; + if(!is_initialized) { + is_initialized = 1; + ret = init_software(); + } + + return ret; } /*---------------------------------------------------------------------------*/ static int diff --git a/platform/jn516x/dev/rtimer-arch-slow.c b/platform/jn516x/dev/rtimer-arch-slow.c new file mode 100644 index 000000000..588edc341 --- /dev/null +++ b/platform/jn516x/dev/rtimer-arch-slow.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2014, SICS Swedish ICT. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ + +/** + * \file + * RTIMER for NXP jn516x: 32 kHz mode + * \author + * Atis Elsts + */ + +#include "sys/rtimer.h" +#include "sys/clock.h" +#include +#include +#include +#include "dev/watchdog.h" +#include "sys/energest.h" +#include "sys/process.h" + +#if RTIMER_USE_32KHZ + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#define RTIMER_TIMER_ISR_DEV E_AHI_DEVICE_SYSCTRL +/* 1.5 days wraparound time */ +#define MAX_VALUE 0xFFFFFFFF +/* make this small to more easily detect wraparound bugs */ +#define START_VALUE (60 * RTIMER_ARCH_SECOND) +#define WRAPAROUND_VALUE ((uint64_t)0x1FFFFFFFFFF) + +static volatile rtimer_clock_t scheduled_time; +static volatile uint8_t has_next; + +/*---------------------------------------------------------------------------*/ +static void +timerISR(uint32 u32Device, uint32 u32ItemBitmap) +{ + PRINTF("\ntimer isr %u %u\n", u32Device, u32ItemBitmap); + if(u32Device != RTIMER_TIMER_ISR_DEV) { + return; + } + + ENERGEST_ON(ENERGEST_TYPE_IRQ); + + if(u32ItemBitmap & TICK_TIMER_MASK) { + /* 32-bit overflow happened; restart the timer */ + uint32_t ticks_late = WRAPAROUND_VALUE - u64AHI_WakeTimerReadLarge(TICK_TIMER); + + PRINTF("\nrtimer oflw, missed ticks %u\n", ticks_late); + + vAHI_WakeTimerStartLarge(TICK_TIMER, MAX_VALUE - ticks_late); + } + + if(u32ItemBitmap & WAKEUP_TIMER_MASK) { + PRINTF("\nrtimer fire @ %u\n", rtimer_arch_now()); + + /* Compare with the current time, as after sleep there is + * a fake interrupt generated 10ms earlier to wake up & reinitialize + * the system before the actual rtimer fires. + */ + rtimer_clock_t now = rtimer_arch_now(); + if(RTIMER_CLOCK_LT(now + 1, scheduled_time)) { + vAHI_WakeTimerEnable(WAKEUP_TIMER, TRUE); + vAHI_WakeTimerStartLarge(WAKEUP_TIMER, scheduled_time - now); + } else { + has_next = 0; + watchdog_start(); + rtimer_run_next(); + process_nevents(); + } + } + + ENERGEST_OFF(ENERGEST_TYPE_IRQ); +} +/*---------------------------------------------------------------------------*/ +void +rtimer_arch_init(void) +{ + /* Initialise tick timer to run continuously */ + vAHI_TickTimerIntEnable(0); + vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_DISABLE); + vAHI_TickTimerWrite(0); + vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_CONT); + + vAHI_SysCtrlRegisterCallback(timerISR); + /* set the highest priority for the rtimer interrupt */ + vAHI_InterruptSetPriority(MICRO_ISR_MASK_SYSCTRL, 15); + /* enable interrupt on a rtimer */ + vAHI_WakeTimerEnable(WAKEUP_TIMER, TRUE); + /* enable interrupt on 32-bit overflow */ + vAHI_WakeTimerEnable(TICK_TIMER, TRUE); + /* count down from START_VALUE */ + vAHI_WakeTimerStartLarge(TICK_TIMER, START_VALUE); + + (void)u32AHI_Init(); +} +/*---------------------------------------------------------------------------*/ +void +rtimer_arch_reinit(rtimer_clock_t sleep_start, rtimer_clock_t sleep_ticks) +{ + uint64_t t; + + uint32_t wakeup_time = sleep_start + (uint64_t)sleep_ticks * (F_CPU / 2) / RTIMER_SECOND; + + /* Initialise tick timer to run continuously */ + vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_DISABLE); + vAHI_TickTimerIntEnable(0); + WAIT_FOR_EDGE(t); + vAHI_TickTimerWrite(wakeup_time); + vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_CONT); + + /* call pending interrupts */ + (void)u32AHI_Init(); + + if(has_next) { + /* reschedule the timer */ + rtimer_arch_schedule(scheduled_time); + } +} +/*---------------------------------------------------------------------------*/ +rtimer_clock_t +rtimer_arch_now(void) +{ + return START_VALUE - (rtimer_clock_t)u64AHI_WakeTimerReadLarge(TICK_TIMER); +} +/*---------------------------------------------------------------------------*/ +void +rtimer_arch_schedule(rtimer_clock_t t) +{ + PRINTF("rtimer_arch_schedule time %lu\n", t); + vAHI_WakeTimerEnable(WAKEUP_TIMER, TRUE); + vAHI_WakeTimerStartLarge(WAKEUP_TIMER, t - rtimer_arch_now()); + scheduled_time = t; + has_next = 1; +} +/*---------------------------------------------------------------------------*/ +rtimer_clock_t +rtimer_arch_time_to_rtimer(void) +{ + rtimer_clock_t now = RTIMER_NOW(); + if(has_next) { + return scheduled_time >= now ? scheduled_time - now : 0; + } + /* if no wakeup is scheduled yet return maximum time */ + return (rtimer_clock_t)-1; +} +/*---------------------------------------------------------------------------*/ +#endif /* RTIMER_USE_32KHZ */ diff --git a/platform/jn516x/dev/rtimer-arch.c b/platform/jn516x/dev/rtimer-arch.c index 6c2e3ae8d..f74fa3fd7 100644 --- a/platform/jn516x/dev/rtimer-arch.c +++ b/platform/jn516x/dev/rtimer-arch.c @@ -35,17 +35,19 @@ * RTIMER for NXP jn516x * \author * Beshr Al Nahas + * Atis Elsts */ #include "sys/rtimer.h" #include "sys/clock.h" -#include "sys/process.h" #include #include +#include #include "dev/watchdog.h" #include "sys/energest.h" +#include "sys/process.h" -#define RTIMER_TIMER_ISR_DEV E_AHI_DEVICE_TICK_TIMER +#if !RTIMER_USE_32KHZ #define DEBUG 0 #if DEBUG @@ -55,16 +57,20 @@ #define PRINTF(...) #endif -static volatile uint32_t compare_time; -static volatile uint32_t last_expired_time; +#define RTIMER_TIMER_ISR_DEV E_AHI_DEVICE_TICK_TIMER + +static volatile rtimer_clock_t scheduled_time; +static volatile uint8_t has_next; void rtimer_arch_run_next(uint32 u32DeviceId, uint32 u32ItemBitmap) { - uint32_t delta, temp; + uint32_t delta; + if(u32DeviceId != RTIMER_TIMER_ISR_DEV) { return; } + ENERGEST_ON(ENERGEST_TYPE_IRQ); vAHI_TickTimerIntPendClr(); vAHI_TickTimerIntEnable(0); @@ -72,27 +78,17 @@ rtimer_arch_run_next(uint32 u32DeviceId, uint32 u32ItemBitmap) * compare register is only 28bits wide so make sure the upper 4bits match * the set compare point */ - delta = u32AHI_TickTimerRead() - compare_time; - if(0 == (delta >> 28)) { - /* compare_time might change after executing rtimer_run_next() - * as some process might schedule the timer - */ - temp = compare_time; - + delta = u32AHI_TickTimerRead() - scheduled_time; + if(delta >> 28 == 0) { /* run scheduled */ + has_next = 0; watchdog_start(); rtimer_run_next(); - - if(process_nevents() > 0) { - /* TODO exit low-power mode */ - } - - watchdog_stop(); - last_expired_time = temp; + process_nevents(); } else { /* No match. Schedule again. */ vAHI_TickTimerIntEnable(1); - vAHI_TickTimerInterval(compare_time); + vAHI_TickTimerInterval(scheduled_time); } ENERGEST_OFF(ENERGEST_TYPE_IRQ); } @@ -101,12 +97,43 @@ void rtimer_arch_init(void) { /* Initialise tick timer to run continuously */ - vAHI_TickTimerIntEnable(0); vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_DISABLE); - last_expired_time = compare_time = 0; - vAHI_TickTimerWrite(0); + vAHI_TickTimerIntEnable(0); vAHI_TickTimerRegisterCallback(rtimer_arch_run_next); + vAHI_TickTimerWrite(0); vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_CONT); + + /* enable wakeup timers, but keep interrupts disabled */ + vAHI_WakeTimerEnable(WAKEUP_TIMER, FALSE); + vAHI_WakeTimerEnable(TICK_TIMER, FALSE); + /* count down from zero (2, as values 0 and 1 must not be used) */ + vAHI_WakeTimerStartLarge(TICK_TIMER, 2); + + (void)u32AHI_Init(); +} +/*---------------------------------------------------------------------------*/ +void +rtimer_arch_reinit(rtimer_clock_t sleep_start, rtimer_clock_t sleep_ticks) +{ + uint64_t t; + /* Initialise tick timer to run continuously */ + vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_DISABLE); + vAHI_TickTimerIntEnable(0); + /* set the highest priority for the rtimer interrupt */ + vAHI_InterruptSetPriority(MICRO_ISR_MASK_TICK_TMR, 15); + vAHI_TickTimerRegisterCallback(rtimer_arch_run_next); + WAIT_FOR_EDGE(t); + vAHI_TickTimerWrite(sleep_start + sleep_ticks); + vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_CONT); + + /* call pending interrupts */ + u32AHI_Init(); + + if(has_next) { + vAHI_TickTimerIntPendClr(); + vAHI_TickTimerIntEnable(1); + vAHI_TickTimerInterval(scheduled_time); + } } /*---------------------------------------------------------------------------*/ rtimer_clock_t @@ -122,18 +149,19 @@ rtimer_arch_schedule(rtimer_clock_t t) vAHI_TickTimerIntPendClr(); vAHI_TickTimerIntEnable(1); vAHI_TickTimerInterval(t); - compare_time = t; + has_next = 1; + scheduled_time = t; } /*---------------------------------------------------------------------------*/ rtimer_clock_t -rtimer_arch_get_time_until_next_wakeup(void) +rtimer_arch_time_to_rtimer(void) { rtimer_clock_t now = RTIMER_NOW(); - rtimer_clock_t next_wakeup = compare_time; - if(bAHI_TickTimerIntStatus()) { - return next_wakeup >= now ? next_wakeup - now : 0; - /* if no wakeup is scheduled yet return maximum time */ + if(has_next) { + return scheduled_time >= now ? scheduled_time - now : 0; } + /* if no wakeup is scheduled yet return maximum time */ return (rtimer_clock_t)-1; } /*---------------------------------------------------------------------------*/ +#endif /* !RTIMER_USE_32KHZ */ diff --git a/platform/jn516x/dev/rtimer-arch.h b/platform/jn516x/dev/rtimer-arch.h index 7e7ad5138..3ffafeeac 100644 --- a/platform/jn516x/dev/rtimer-arch.h +++ b/platform/jn516x/dev/rtimer-arch.h @@ -35,6 +35,7 @@ * Header file for NXP jn516x-specific rtimer code * \author * Beshr Al Nahas + * Atis Elsts */ #ifndef RTIMER_ARCH_H_ @@ -43,18 +44,75 @@ #include "sys/rtimer.h" #ifdef RTIMER_CONF_SECOND -#define RTIMER_ARCH_SECOND RTIMER_CONF_SECOND +# define RTIMER_ARCH_SECOND RTIMER_CONF_SECOND +#else +#if RTIMER_USE_32KHZ +# if JN516X_EXTERNAL_CRYSTAL_OSCILLATOR +# define RTIMER_ARCH_SECOND 32768 +# else +# define RTIMER_ARCH_SECOND 32000 +#endif #else /* 32MHz CPU clock => 16MHz timer */ -#define RTIMER_ARCH_SECOND (F_CPU / 2) +# define RTIMER_ARCH_SECOND (F_CPU / 2) +#endif #endif -#define US_TO_RTIMERTICKS(D) ((int64_t)(D) << 4) -#define RTIMERTICKS_TO_US(T) ((int64_t)(T) >> 4) -#define RTIMERTICKS_TO_US_64(T) RTIMERTICKS_TO_US(T) +#if RTIMER_USE_32KHZ +#define US_TO_RTIMERTICKS(US) ((US) >= 0 ? \ + (((int32_t)(US) * (RTIMER_ARCH_SECOND) + 500000) / 1000000L) : \ + ((int32_t)(US) * (RTIMER_ARCH_SECOND) - 500000) / 1000000L) + +#define RTIMERTICKS_TO_US(T) ((T) >= 0 ? \ + (((int32_t)(T) * 1000000L + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)) : \ + ((int32_t)(T) * 1000000L - ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)) + +/* A 64-bit version because the 32-bit one cannot handle T >= 4295 ticks. + Intended only for positive values of T. */ +#define RTIMERTICKS_TO_US_64(T) ((uint32_t)(((uint64_t)(T) * 1000000 + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND))) + +#else + +#define US_TO_RTIMERTICKS(D) ((int64_t)(D) << 4) +#define RTIMERTICKS_TO_US(T) ((int64_t)(T) >> 4) +#define RTIMERTICKS_TO_US_64(T) RTIMERTICKS_TO_US(T) + +#endif rtimer_clock_t rtimer_arch_now(void); -rtimer_clock_t rtimer_arch_get_time_until_next_wakeup(void); +rtimer_clock_t rtimer_arch_time_to_rtimer(void); + +void rtimer_arch_reinit(rtimer_clock_t sleep_start, rtimer_clock_t wakeup_time); + +void clock_arch_init(int is_reinitialization); + +void clock_arch_calibrate(void); + +void clock_arch_reinit(void); + +void clock_arch_schedule_interrupt(clock_time_t time_to_etimer, rtimer_clock_t ticks_to_rtimer); + +clock_t clock_arch_time_to_etimer(void); + +/* Use 20 ms: enough for TSCH with the default schedule to sleep */ +#define JN516X_MIN_SLEEP_TIME (RTIMER_SECOND / 50) +/* 1 second by default: arbitrary picked value which could be increased */ +#define JN516X_MAX_SLEEP_TIME RTIMER_SECOND +/* Assume conservative 10 ms maximal system wakeup time */ +#define JN516X_SLEEP_GUARD_TIME (RTIMER_ARCH_SECOND / 100) + +#define WAKEUP_TIMER E_AHI_WAKE_TIMER_0 +#define WAKEUP_TIMER_MASK E_AHI_SYSCTRL_WK0_MASK + +#define TICK_TIMER E_AHI_WAKE_TIMER_1 +#define TICK_TIMER_MASK E_AHI_SYSCTRL_WK1_MASK + +#define WAIT_FOR_EDGE(edge_t) do { \ + uint64_t start_t = u64AHI_WakeTimerReadLarge(TICK_TIMER); \ + do { \ + edge_t = u64AHI_WakeTimerReadLarge(TICK_TIMER); \ + } while(edge_t == start_t); \ + } while(0) #endif /* RTIMER_ARCH_H_ */ diff --git a/platform/jn516x/dev/uart-driver.h b/platform/jn516x/dev/uart-driver.h index 322bf950d..1953926a3 100644 --- a/platform/jn516x/dev/uart-driver.h +++ b/platform/jn516x/dev/uart-driver.h @@ -39,6 +39,8 @@ #include #include "contiki-conf.h" +#define UART_EXTRAS 1 + void uart_driver_init(uint8_t uart_dev, uint8_t br, uint8_t * txbuf_data, uint16_t txbuf_size, uint8_t * rxbuf_data, uint16_t rxbuf_size, int (*uart_input_function)(unsigned char c)); void uart_driver_write_buffered(uint8_t uart_dev, uint8_t ch); void uart_driver_write_with_deadline(uint8_t uart_dev, uint8_t c); diff --git a/platform/jn516x/platform-conf.h b/platform/jn516x/platform-conf.h index fa289fc2f..da85202d8 100644 --- a/platform/jn516x/platform-conf.h +++ b/platform/jn516x/platform-conf.h @@ -44,6 +44,8 @@ /* Delay between GO signal and start listening * Measured 104us: between GO signal and start listening */ #define RADIO_DELAY_BEFORE_RX ((unsigned)US_TO_RTIMERTICKS(104)) +/* Delay between the SFD finishes arriving and it is detected in software */ +#define RADIO_DELAY_BEFORE_DETECT ((unsigned)US_TO_RTIMERTICKS(14)) /* Micromac configuration */ @@ -59,9 +61,57 @@ #define MICROMAC_CONF_CHANNEL RF_CHANNEL #endif -/* Timer conversion - * RTIMER 16M = 256 * 62500(RADIO) == 2^8 * 62500 */ -#define RADIO_TO_RTIMER(X) ((rtimer_clock_t)((X) << (int32_t)8L)) +/* 32kHz or 16MHz rtimers? */ +#ifdef RTIMER_CONF_USE_32KHZ +#define RTIMER_USE_32KHZ RTIMER_CONF_USE_32KHZ +#else +#define RTIMER_USE_32KHZ 0 +#endif + +/* Put the device in a sleep mode in idle periods? + * If RTIMER_USE_32KHZ is set, the device runs all the time on the 32 kHz oscillator. + * If RTIMER_USE_32KHZ is not set, the device runs on the 32 kHz oscillator during sleep, + * and switches back to the 32 MHz oscillator (16 MHz rtimer) at wakeup. + * */ +#ifdef JN516X_SLEEP_CONF_ENABLED +#define JN516X_SLEEP_ENABLED JN516X_SLEEP_CONF_ENABLED +#else +#define JN516X_SLEEP_ENABLED 0 +#endif + +/* Enable this to get the 32.768kHz oscillator */ +#ifndef JN516X_EXTERNAL_CRYSTAL_OSCILLATOR +#define JN516X_EXTERNAL_CRYSTAL_OSCILLATOR (RTIMER_USE_32KHZ || JN516X_SLEEP_ENABLED) +#endif /* JN516X_EXTERNAL_CRYSTAL_OSCILLATOR */ + +/* Core rtimer.h defaults to 16 bit timer unless RTIMER_CLOCK_LT is defined */ +typedef uint32_t rtimer_clock_t; +#define RTIMER_CLOCK_LT(a, b) ((int32_t)((a) - (b)) < 0) + +/* 8ms timer tick */ +#define CLOCK_CONF_SECOND 125 + +#if JN516X_EXTERNAL_CRYSTAL_OSCILLATOR +#define JN516X_XOSC_SECOND 32768 +#else +#define JN516X_XOSC_SECOND 32000 +#endif + +/* Timer conversion*/ +#if RTIMER_USE_32KHZ +#define RADIO_TO_RTIMER(X) ((X) * (JN516X_XOSC_SECOND) / 62500) +#else + /* RTIMER 16M = 256 * 62500(RADIO) == 2^8 * 62500 */ +#define RADIO_TO_RTIMER(X) ((rtimer_clock_t)((X) << (int32_t)8L)) +#endif + +/* If the timer base a binary 32kHz clock, compensate for this base drift */ +#if RTIMER_USE_32KHZ && JN516X_EXTERNAL_CRYSTAL_OSCILLATOR +/* Drift calculated using this formula: +* ((US_TO_TICKS(10000) * 100) - RTIMER_SECOND) * 1e6 = 976.5625 ppm +*/ +#define TSCH_CONF_BASE_DRIFT_PPM -977 +#endif #define DR_11744_DIO2 12 #define DR_11744_DIO3 13 @@ -70,6 +120,13 @@ #define DR_11744_DIO6 16 #define DR_11744_DIO7 17 +/* Enable power amplifier of JN5168 M05 and M06 modules */ +#if defined(JN5168_M05) || defined(JN5168_M06) +#define RADIO_TEST_MODE RADIO_TEST_MODE_HIGH_PWR +#else +#define RADIO_TEST_MODE RADIO_TEST_MODE_DISABLED +#endif + #define TSCH_DEBUG 0 #if TSCH_DEBUG @@ -183,14 +240,10 @@ typedef long long int64_t; typedef uint16_t uip_stats_t; typedef uint32_t clock_time_t; -/* Core rtimer.h defaults to 16 bit timer unless RTIMER_CLOCK_LT is defined */ -typedef uint32_t rtimer_clock_t; -#define RTIMER_CLOCK_LT(a, b) ((int32_t)((a) - (b)) < 0) -/* 10ms timer tick */ -#define CLOCK_CONF_SECOND 100 - /* Shall we calibrate the DCO periodically? */ +#ifndef DCOSYNCH_CONF_ENABLED #define DCOSYNCH_CONF_ENABLED 1 +#endif /* How often shall we attempt to calibrate DCO? * PS: It should be calibrated upon temperature changes, @@ -223,11 +276,6 @@ typedef uint32_t rtimer_clock_t; #define SLIP_BRIDGE_CONF_NO_PUTCHAR 1 #endif /* SLIP_BRIDGE_CONF_NO_PUTCHAR */ -/* Enable this to get the 32.768kHz oscillator */ -#ifndef USE_EXTERNAL_OSCILLATOR -#define USE_EXTERNAL_OSCILLATOR 0 -#endif /* USE_EXTERNAL_OSCILLATOR */ - /* Extension of LED definitions from leds.h for various JN516x dev boards JN516x Dongle: LEDS_RED Red LED on dongle From bef32c20fb6c8717fbf7d82b5138bbb72c818ed6 Mon Sep 17 00:00:00 2001 From: Simon Duquennoy Date: Wed, 2 Dec 2015 10:26:06 +0100 Subject: [PATCH 2/2] jn516x examples: print out network state periodically --- .../jn516x/rpl/border-router/border-router.c | 8 ++++ examples/jn516x/rpl/node/node.c | 15 ++++-- examples/jn516x/rpl/tools/rpl-tools.c | 48 +++++++++++++++++++ examples/jn516x/rpl/tools/rpl-tools.h | 1 + 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/examples/jn516x/rpl/border-router/border-router.c b/examples/jn516x/rpl/border-router/border-router.c index a260aef3b..a55a3241e 100644 --- a/examples/jn516x/rpl/border-router/border-router.c +++ b/examples/jn516x/rpl/border-router/border-router.c @@ -93,6 +93,14 @@ PROCESS_THREAD(border_router_process, ev, data) rpl_tools_init(&prefix); + /* Print out routing tables every minute */ + etimer_set(&et, CLOCK_SECOND * 60); + while(1) { + print_network_status(); + PROCESS_YIELD_UNTIL(etimer_expired(&et)); + etimer_reset(&et); + } + PROCESS_END(); } /*---------------------------------------------------------------------------*/ diff --git a/examples/jn516x/rpl/node/node.c b/examples/jn516x/rpl/node/node.c index e4fccec7d..143abc18f 100644 --- a/examples/jn516x/rpl/node/node.c +++ b/examples/jn516x/rpl/node/node.c @@ -58,6 +58,7 @@ AUTOSTART_PROCESSES(&node_process); /*---------------------------------------------------------------------------*/ PROCESS_THREAD(node_process, ev, data) { + static struct etimer et; PROCESS_BEGIN(); /* 3 possible roles: @@ -71,8 +72,6 @@ PROCESS_THREAD(node_process, ev, data) #if CONFIG_VIA_BUTTON { #define CONFIG_WAIT_TIME 10 - static struct etimer et; - SENSORS_ACTIVATE(button_sensor); etimer_set(&et, CLOCK_SECOND * CONFIG_WAIT_TIME); @@ -103,6 +102,16 @@ PROCESS_THREAD(node_process, ev, data) rpl_tools_init(&prefix); } else { rpl_tools_init(NULL); - } PROCESS_END(); + } + + /* Print out routing tables every minute */ + etimer_set(&et, CLOCK_SECOND * 60); + while(1) { + print_network_status(); + PROCESS_YIELD_UNTIL(etimer_expired(&et)); + etimer_reset(&et); + } + + PROCESS_END(); } /*---------------------------------------------------------------------------*/ diff --git a/examples/jn516x/rpl/tools/rpl-tools.c b/examples/jn516x/rpl/tools/rpl-tools.c index 23a041306..dff045ab6 100644 --- a/examples/jn516x/rpl/tools/rpl-tools.c +++ b/examples/jn516x/rpl/tools/rpl-tools.c @@ -43,6 +43,54 @@ #define DEBUG DEBUG_PRINT #include "net/ip/uip-debug.h" +/*---------------------------------------------------------------------------*/ +void +print_network_status(void) +{ + int i; + uint8_t state; + uip_ds6_defrt_t *default_route; + uip_ds6_route_t *route; + + PRINTA("--- Network status ---\n"); + + /* Our IPv6 addresses */ + PRINTA("- Server IPv6 addresses:\n"); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && + (state == ADDR_TENTATIVE || state == ADDR_PREFERRED)) { + PRINTA("-- "); + uip_debug_ipaddr_print(&uip_ds6_if.addr_list[i].ipaddr); + PRINTA("\n"); + } + } + + /* Our default route */ + PRINTA("- Default route:\n"); + default_route = uip_ds6_defrt_lookup(uip_ds6_defrt_choose()); + if(default_route != NULL) { + PRINTA("-- "); + uip_debug_ipaddr_print(&default_route->ipaddr);; + PRINTA(" (lifetime: %lu seconds)\n", (unsigned long)default_route->lifetime.interval); + } else { + PRINTA("-- None\n"); + } + + /* Our routing entries */ + PRINTA("- Routing entries (%u in total):\n", uip_ds6_route_num_routes()); + route = uip_ds6_route_head(); + while(route != NULL) { + PRINTA("-- "); + uip_debug_ipaddr_print(&route->ipaddr); + PRINTA(" via "); + uip_debug_ipaddr_print(uip_ds6_route_nexthop(route)); + PRINTA(" (lifetime: %lu seconds)\n", (unsigned long)route->state.lifetime); + route = uip_ds6_route_next(route); + } + + PRINTA("----------------------\n"); +} /*---------------------------------------------------------------------------*/ static void print_local_addresses(void) diff --git a/examples/jn516x/rpl/tools/rpl-tools.h b/examples/jn516x/rpl/tools/rpl-tools.h index e91bc81c5..f14ec1d86 100644 --- a/examples/jn516x/rpl/tools/rpl-tools.h +++ b/examples/jn516x/rpl/tools/rpl-tools.h @@ -33,3 +33,4 @@ */ void rpl_tools_init(uip_ipaddr_t *br_prefix); +void print_network_status(void);