The clock adjustments made when waking up from PM1/2 were very inaccurate. If
relying on ContikiMAC's rtimer to sleep, this led to Contiki's software clock
time, seconds and etimers to be 2.5 s slower after each min, i.e. 1 hour slower
after each day, which is a show stopper issue for most real-life applications.
This was caused by a lack of accuracy in several pieces of code during sleep
entry and wake-up:
- It was difficult to synchronize the calls to RTIMER_NOW() before and after
sleep with the deactivation and activation of the SysTick peripheral caused
by PM1/2. This caused an inaccuracy in the corrective number of ticks passed
to clock_adjust().
- The value passed to clock_adjust() was truncated from an rtimer_clock_t
value, but the accumulated error caused by these truncated bits was ignored.
- The SysTick peripheral had to be stopped during the call to clock_adjust().
Rather than creating even more complicated clock adjustment mechanisms that
would probably still have mixed results as to accuracy, this change simply uses
the Sleep Timer counter as a base value for Contiki's clock and seconds
counters. The tick from the Systick peripheral is still used as the interrupt
source to update Contiki's clocks and timers. When running, the SysTick
peripheral and the Sleep Timer are synchronized, so combining both is not an
issue, and this allows not to alter the rtimer interrupt mechanism using the
Sleep Timer. The purpose of the Sleep Timer is to be an RTC, so it is the
perfect fit for the clock module, all the more it can not be disturbed by PM1/2.
If the 32-kHz XOSC is used, the Sleep Timer is also very accurate. If the
32-kHZ RCOSC is used, it is calibrated from the 32-MHz XOSC, so it is also
accurate, and the 32753-Hz vs. 32768-Hz systematic error in that case is
negligible, all the more one would use the 32-kHz XOSC for better accuracy.
Besides fixing this time drift issue, this change has several benefits:
- clock_time(), clock_seconds() and RTIMER_NOW() start synchronized, and they
change at the same source pace.
- If clock_set_seconds() is called, then clock_seconds() indicates one more
second almost exactly one second later, then exactly each second. Before this
change, clock_seconds() was not synchronized with clock_set_seconds(), so the
value returned by the former could be incremented immediately after the call
to the latter in some cases.
- The code tied to the clock module is simpler and more robust.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Whole elapsed seconds are added to secs first, so only the remaining subsecond
ticks should then be subtracted from second_countdown in order to decide whether
secs should be incremented again.
Otherwise, secs is not correctly updated in some cases, typically if the bit 7
of ticks is 1. E.g., with ticks = 128 (i.e. exactly 1 s elapsed) and
second_countdown = 128, secs was first incremented as expected, then 128 was
subtracted from second_countdown, giving 0 and triggering an unwanted second
increment of secs. Or with ticks = 129 (i.e. 1 s + 1 tick) and
second_countdown = 1, secs was first incremented as expected, then 129 was
subtracted from second_countdown, giving 128 and missing a second increment of
secs that should have occurred because second_countdown wrapped around.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
During PM1+, the hardware timer used to implement the Contiki clock is frozen,
so clock_adjust() needs to be called when exiting those modes in order to
compensate for the clock ticks missed while the timer was frozen. Doing so
changes the Contiki clock time, so etimer_request_poll() needs to be called in
order to inform the etimer library that an etimer might have expired.
Note that waiting for the next clock ISR to call etimer_request_poll() is
unreliable because the system might go back to sleep beforehand.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
When returning from PM1/2, the sleep timer value (used by RTIMER_NOW()) is not
up-to-date until a positive edge on the 32-kHz clock has been detected after the
system clock restarted. To ensure an updated value is read, wait for a positive
transition on the 32-kHz clock by polling the SYS_CTRL_CLOCK_STA.SYNC_32K bit,
before reading the sleep timer value.
Because of this RTIMER_NOW() fixup, lpm_exit() has to be called at the very
beginning of ISRs waking up the SoC. This also ensures that all clocks and
timers are enabled at the correct frequency and updated before using them
following wake-up.
Without this fix, etimers could sometimes (randomly, depending on timings)
become ultra slow (observed from 10x to 40x slower than normal) if the system
exited PM1/2 very often. This issue occurred more often with PM1.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
As recommended by the CC2538 User's Guide, set SYS_CTRL_CLOCK_CTRL.OSC_PD to 0
before asserting WFI, and set it to 1 after the system clock is sourced from the
32-MHz XOSC following wake-up. This allows to automatically start both
oscillators upon wake-up in order to partially hide the 32-MHz XOSC startup time
by the 16-MHz RCOSC startup time.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
As a matter of precaution, always make sure that pending system clock
transitions are complete before requesting a new change of the system clock
source.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
In one of the abort cases in lpm_enter(), the energest context has previously
been set to LPM, so the abort code needs to set it back to CPU.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Create a dedicated header file with all the definitions for the flash lock bit
page and customer configuration area. This avoids duplicating those definitions
in the startup-gcc.c files of all CC2538-based platforms, and this also allows
to easily manipulate the CCA from outside startup-gcc.c (e.g. for on-the-air
firmware update).
The definitions are now complete contrary to what was in startup-gcc.c:
- Definitions have been added to select the bootloader backdoor pin and active
level if enabled.
- Definitions have been added to access the page and debug lock bits. The debug
lock bit can be used to prevent someone from reading back a programmed
firmware through JTAG if the firmware binary image has to be confidential,
which should be combined with a disabled bootloader backdoor.
- The application entry point is now tied to the beginning of the .text section
instead of to the beginning of the flash. This allows projects using custom
linker scripts to place the application entry point anywhere in the flash,
which can be useful e.g. for on-the-air firmware update.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
The pending GPIO power-up interrupts have to be cleared in the ISRs in order not
to re-trigger the interrupts and the wake-up events.
The power-up interrupts of all pins are cleared for each port in the
corresponding port ISR. This is done after calling the registered callbacks so
that the callbacks can know which pin woke up the SoC. This is done after
clearing the regular interrupt in order to avoid getting a new wake-up interrupt
without the regular interrupt in the case of a new wake-up edge occurring
between the two clears.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
The GPIO power-up interrupts have to be configured and enabled in order to be
able to wake-up the SoC from PM1+ upon a signal edge occurring on a GPIO input
pin.
This set of macros allows to:
- configure the signal edge triggering a power-up interrupt,
- enable and disable a power-up interrupt,
- clear a power-up interrupt flag.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
If PM2 is enabled with LPM_CONF_MAX_PM, but not active, the non-retention area
of the SRAM can be useful to place temporary data that does not fit in the
low-leakage SRAM, typically after having called lpm_set_max_pm(LPM_PM1). Hence,
give access to this non-retention area thanks to .nrdata* sections.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
The data sheet recommends that the USB pull-up resistor be driven by a GPIO so
that it can be controlled by software, but this is not mandatory. Hence, leave
the choice so that CC253-based boards not using this option can build and work
fine.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Homogenize port and pin definitions naming:
- PERIPHERAL_FUNCTION_PORT for the port ID,
- PERIPHERAL_FUNCTION_PIN for the pin ID,
- PERIPHERAL_FUNCTION_PORT_BASE for the port base,
- PERIPHERAL_FUNCTION_PIN_MASK for the pin mask.
Define only PERIPHERAL_FUNCTION_PORT and PERIPHERAL_FUNCTION_PIN in board.h, and
deduce PERIPHERAL_FUNCTION_PORT_BASE and PERIPHERAL_FUNCTION_PIN_MASK in the
driver from the former definitions.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Use the GPIO accessor macros instead of copying raw register access code all
over the place. This is cleaner and less error prone.
This fixes the setting of the USB pull-up resistor that worked only by chance on
the CC2538DK because it is controlled by the pin 0 of the used GPIO port.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Introduce new useful GPIO macros to:
- read the levels of some port pins,
- write the levels of some port pins (pass bit-field value to be set),
- clear the interrupt flags for some port pins.
These macros are cleaner and less error prone than raw register access code
copied all over the place.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
The parameters in the GPIO macros were used without being parenthesized. This
could generate wrong values for register assignments in the case of expressions
passed as arguments to these macros.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
lpm_enter() must not enter PM1+ if the UART TX FIFO is not empty. Otherwise, the
UART clock gets disabled, and its TX is broken.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Some peripherals have their clocks automatically gated in PM1+ modes, so they
cannot operate. This new mechanism gives peripherals a way to prohibit PM1+
modes so that they can properly complete their current operations before
entering PM1+.
This mechanism is implemented with peripheral functions registered to the LPM
module. These functions return whether the associated peripheral permits or not
PM1+ modes. They are called by the LPM module each time PM1+ might be possible.
If any of the peripherals wants to block PM1+, then the system is only dropped
to PM0.
Partly from: George Oikonomou
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
spi-arch.h configures dev/spi.h, so it must be #included first. Luckily, this
mistake did not have any consequence here, but fix it in order to avoid possible
future issues.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
If the SSI has never been used and spi_init() is called, then the SSI receive
FIFO is empty and remains so, so calling SPI_WAITFOREORx() at the end of
spi_init() waits endlessly for SSI_SR.RNE to be set. Hence, this call must be
removed in order to avoid a deadlock.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
ELF files generated by GCC make SmartRF Flash Programmer 2 crash (only the TI
format is supported by this tool for ELFs), and binary files are not very
appropriate because they are gapless, so generate Intel HEX files since these
are very well supported by most programming tools while still flexible.
Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
This uses the core/dev/spi.h header and implements the spi_init()
function and the various macros for SPI operation. ssi.h contains all of
the register locations and information.
This implementation is not very versatile, mostly because I don't how to
make it flexible in the contiki system. It supports pin muxing for the
four spi pins, but other than that picks sensible defaults.
The SPI macros (like SPI_READ()) are defined in
cpu/cc2538/spi-arch.h. In order to use the SPI driver, add the following
includes to your project:
#include "spi-arch.h
#include "dev/spi.h"
Historically $(OBJECTDIR) was created when Makefile.include is read. A
consequence is that combining "clean" with "all" (or any other build
target) results in an error because the clean removes the object
directory that is required to exist when building dependencies.
Creating $(OBJECTDIR) on-demand ensures it is present when needed.
Removed creation of $(OBJECTDIR) on initial read, and added an order-only
dependency forcing its creation all Makefile* rules where the target is
explicitly or implicitly in $(OBJECTDIR).