Skip to content

NRF: Persist RTC offset across reboots #2924

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion ports/nrf/boards/common.template.ld
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ SECTIONS
_edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */
} >RAM

/* Uninitialized data section */
/* Zero-initialized data section */
.bss :
{
. = ALIGN(4);
Expand All @@ -116,6 +116,19 @@ SECTIONS
_ebss = .; /* define a global symbol at bss end; used by startup code and GC */
} >RAM

/* Uninitialized data section
Data placed into this section will remain unchanged across reboots. */
.uninitialized (NOLOAD) :
{
. = ALIGN(4);
_suninitialized = .; /* define a global symbol at uninitialized start; currently unused */
*(.uninitialized)
*(.uninitialized*)

. = ALIGN(4);
_euninitialized = .; /* define a global symbol at uninitialized end; currently unused */
} >RAM

/* this is to define the start of the heap, and make sure we have a minimum size */
.heap :
{
Expand Down
29 changes: 26 additions & 3 deletions ports/nrf/common-hal/rtc/RTC.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,42 @@
#include "supervisor/shared/translate.h"

// This is the time in seconds since 2000 that the RTC was started.
static uint32_t rtc_offset = 0;
__attribute__((section(".uninitialized"))) static uint32_t rtc_offset[3];

// These values are placed before and after the current RTC count. They are
// used to determine if the RTC count is valid. These randomly-generated values
// will be set when the RTC value is set in order to mark the RTC as valid. If
// the system crashes or reboots, these values will remain undisturbed and the
// RTC offset will remain valid.
//
// If Circuit Python is updated or these symbols shift around, the prefix and
// suffix will no longer match, and the time will no longer be valid.
#define RTC_OFFSET_CHECK_PREFIX 0x25ea7e2a
#define RTC_OFFSET_CHECK_SUFFIX 0x2b80b69e

void common_hal_rtc_init(void) {
// If the prefix and suffix are not valid, zero-initialize the RTC offset.
if ((rtc_offset[0] != RTC_OFFSET_CHECK_PREFIX) || (rtc_offset[2] != RTC_OFFSET_CHECK_SUFFIX))
rtc_offset[1] = 0;
}

void common_hal_rtc_get_time(timeutils_struct_time_t *tm) {
uint64_t ticks_s = port_get_raw_ticks(NULL) / 1024;
timeutils_seconds_since_2000_to_struct_time(rtc_offset + ticks_s, tm);
timeutils_seconds_since_2000_to_struct_time(rtc_offset[1] + ticks_s, tm);
}

void common_hal_rtc_set_time(timeutils_struct_time_t *tm) {
uint64_t ticks_s = port_get_raw_ticks(NULL) / 1024;
uint32_t epoch_s = timeutils_seconds_since_2000(
tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec
);
rtc_offset = epoch_s - ticks_s;
rtc_offset[1] = epoch_s - ticks_s;

// Set the prefix and suffix in order to indicate the time is valid. This
// must be done after the offset is updated, in case there is a crash or
// power failure.
rtc_offset[0] = RTC_OFFSET_CHECK_PREFIX;
rtc_offset[2] = RTC_OFFSET_CHECK_SUFFIX;
}

int common_hal_rtc_get_calibration(void) {
Expand Down
1 change: 1 addition & 0 deletions ports/nrf/common-hal/rtc/RTC.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@

extern void rtc_init(void);
extern void rtc_reset(void);
extern void common_hal_rtc_init(void);

#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_RTC_RTC_H
21 changes: 15 additions & 6 deletions ports/nrf/supervisor/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ safe_mode_t port_init(void) {
// Configure millisecond timer initialization.
tick_init();

#if CIRCUITPY_RTC
common_hal_rtc_init();
#endif

#if CIRCUITPY_ANALOGIO
analogin_init();
#endif
Expand Down Expand Up @@ -177,30 +181,35 @@ void reset_cpu(void) {
NVIC_SystemReset();
}

// The uninitialized data section is placed directly after BSS, under the theory
// that Circuit Python has a lot more .data and .bss than the bootloader. As a
// result, this section is less likely to be tampered with by the bootloader.
extern uint32_t _euninitialized;

uint32_t *port_heap_get_bottom(void) {
return port_stack_get_limit();
return &_euninitialized;
}

uint32_t *port_heap_get_top(void) {
return port_stack_get_top();
}

uint32_t *port_stack_get_limit(void) {
return &_ebss;
return &_euninitialized;
}

uint32_t *port_stack_get_top(void) {
return &_estack;
}

extern uint32_t _ebss;
// Place the word to save just after our BSS section that gets blanked.
// Place the word in the uninitialized section so it won't get overwritten.
__attribute__((section(".uninitialized"))) uint32_t _saved_word;
void port_set_saved_word(uint32_t value) {
_ebss = value;
_saved_word = value;
}

uint32_t port_get_saved_word(void) {
return _ebss;
return _saved_word;
}

uint64_t port_get_raw_ticks(uint8_t* subticks) {
Expand Down