Skip to content

Commit bdba2e6

Browse files
pabigotcarlescufi
authored andcommitted
samples: nrf: system_off: add RAM retention example for nRF52
Add an object to retain data across both reboots and entry to SYSTEM_OFF. This only works on nRF52, since nRF51 and nRF53 require different low-level operations to configure RAM retention. Signed-off-by: Peter Bigot <[email protected]>
1 parent 42cf4c7 commit bdba2e6

File tree

8 files changed

+287
-3
lines changed

8 files changed

+287
-3
lines changed

samples/boards/nrf/system_off/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ cmake_minimum_required(VERSION 3.13.1)
44
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
55
project(nrf_system_off)
66

7-
FILE(GLOB app_sources src/*.c)
8-
target_sources(app PRIVATE ${app_sources})
7+
target_sources(app PRIVATE src/main.c)
8+
target_sources_ifdef(CONFIG_APP_RETENTION app PRIVATE src/retained.c)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright (c) 2021 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
mainmenu "Nordic SYSTEM_OFF demo"
5+
6+
config APP_RETENTION
7+
bool "Enable state retention in system off"
8+
depends on SOC_COMPATIBLE_NRF52X
9+
help
10+
On some Nordic chips this application supports retaining
11+
memory while in system off. Select this to enable the
12+
feature.
13+
14+
source "Kconfig.zephyr"

samples/boards/nrf/system_off/README.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ disable the deep sleep functionality before the kernel starts, which
2222
prevents the board from powering down during initialization of drivers
2323
that use unbounded delays to wait for startup.
2424

25+
RAM Retention
26+
=============
27+
28+
On nRF52 platforms this also can demonstrate RAM retention. By selecting
29+
``CONFIG_APP_RETENTION=y`` state related to number of boots, number of times
30+
system off was entered, and total uptime since initial power-on are retained
31+
in a checksummed data structure. The POWER peripheral is configured to keep
32+
the containing RAM section powered while in system-off mode.
33+
2534
Requirements
2635
************
2736

samples/boards/nrf/system_off/prj.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ CONFIG_PM=y
22
# Required to disable default behavior of deep sleep on timeout
33
CONFIG_PM_DEVICE=y
44
CONFIG_GPIO=y
5+
# Optional select RAM retention (nRF52 only)
6+
#CONFIG_APP_RETENTION=y
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
sample:
22
name: Low Power State Sample for nRF5x
3+
common:
4+
tags: power
35
tests:
46
sample.boards.nrf.system_off:
57
build_only: true
68
platform_allow: nrf52840dk_nrf52840 nrf52dk_nrf52832 nrf51dk_nrf51422
7-
tags: power
9+
sample.boards.nrf.system_off.retained:
10+
build_only: true
11+
platform_allow: nrf52840dk_nrf52840 nrf52dk_nrf52832
12+
extra_configs:
13+
- CONFIG_APP_RETENTION=y

samples/boards/nrf/system_off/src/main.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <device.h>
1010
#include <init.h>
1111
#include <power/power.h>
12+
#include "retained.h"
1213
#include <hal/nrf_gpio.h>
1314

1415
#define CONSOLE_LABEL DT_LABEL(DT_CHOSEN(zephyr_console))
@@ -40,6 +41,21 @@ void main(void)
4041

4142
printk("\n%s system off demo\n", CONFIG_BOARD);
4243

44+
if (IS_ENABLED(CONFIG_APP_RETENTION)) {
45+
bool retained_ok = retained_validate();
46+
47+
/* Increment for this boot attempt and update. */
48+
retained.boots += 1;
49+
retained_update();
50+
51+
printk("Retained data: %s\n", retained_ok ? "valid" : "INVALID");
52+
printk("Boot count: %u\n", retained.boots);
53+
printk("Off count: %u\n", retained.off_count);
54+
printk("Active Ticks: %" PRIu64 "\n", retained.uptime_sum);
55+
} else {
56+
printk("Retained data not supported\n");
57+
}
58+
4359
/* Configure to generate PORT event (wakeup) on button 1 press. */
4460
nrf_gpio_cfg_input(DT_GPIO_PIN(DT_NODELABEL(button0), gpios),
4561
NRF_GPIO_PIN_PULLUP);
@@ -64,6 +80,12 @@ void main(void)
6480

6581
printk("Entering system off; press BUTTON1 to restart\n");
6682

83+
if (IS_ENABLED(CONFIG_APP_RETENTION)) {
84+
/* Update the retained state */
85+
retained.off_count += 1;
86+
retained_update();
87+
}
88+
6789
/* Above we disabled entry to deep sleep based on duration of
6890
* controlled delay. Here we need to override that, then
6991
* force entry to deep sleep on any delay.
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright (c) 2021 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <string.h>
8+
#include <zephyr.h>
9+
#include <devicetree.h>
10+
#include <sys/byteorder.h>
11+
#include <sys/crc.h>
12+
#include <hal/nrf_power.h>
13+
#include "retained.h"
14+
15+
/* nRF52 RAM (really, RAM AHB slaves) are partitioned as:
16+
* * Up to 8 blocks of two 4 KiBy byte "small" sections
17+
* * A 9th block of with 32 KiBy "large" sections
18+
*
19+
* At time of writing the maximum number of large sections is 6, all
20+
* within the first large block. Theoretically there could be more
21+
* sections in the 9th block, and possibly more blocks.
22+
*/
23+
24+
/* Inclusive address of RAM start */
25+
#define SRAM_BEGIN (uintptr_t)DT_REG_ADDR(DT_NODELABEL(sram0))
26+
27+
/* Exclusive address of RAM end */
28+
#define SRAM_END (SRAM_BEGIN + (uintptr_t)DT_REG_SIZE(DT_NODELABEL(sram0)))
29+
30+
/* Size of a controllable RAM section in the small blocks */
31+
#define SMALL_SECTION_SIZE 4096
32+
33+
/* Number of controllable RAM sections in each of the lower blocks */
34+
#define SMALL_SECTIONS_PER_BLOCK 2
35+
36+
/* Span of a small block */
37+
#define SMALL_BLOCK_SIZE (SMALL_SECTIONS_PER_BLOCK * SMALL_SECTION_SIZE)
38+
39+
/* Number of small blocks */
40+
#define SMALL_BLOCK_COUNT 8
41+
42+
/* Span of the SRAM area covered by small sections */
43+
#define SMALL_SECTION_SPAN (SMALL_BLOCK_COUNT * SMALL_BLOCK_SIZE)
44+
45+
/* Inclusive address of the RAM range covered by large sections */
46+
#define LARGE_SECTION_BEGIN (SRAM_BEGIN + SMALL_SECTION_SPAN)
47+
48+
/* Size of a controllable RAM section in large blocks */
49+
#define LARGE_SECTION_SIZE 32768
50+
51+
/* Set or clear RAM retention in SYSTEM_OFF for the provided object.
52+
*
53+
* @note This only works for nRF52 with the POWER module. The other
54+
* Nordic chips use a different low-level API, which is not currently
55+
* used by this function.
56+
*
57+
* @param ptr pointer to the start of the retainable object
58+
*
59+
* @param len length of the retainable object
60+
*
61+
* @param enable true to enable retention, false to clear retention
62+
*/
63+
static int ram_range_retain(const void *ptr,
64+
size_t len,
65+
bool enable)
66+
{
67+
uintptr_t addr = (uintptr_t)ptr;
68+
uintptr_t addr_end = addr + len;
69+
70+
/* Error if the provided range is empty or doesn't lie
71+
* entirely within the SRAM address space.
72+
*/
73+
if ((len == 0U)
74+
|| (addr < SRAM_BEGIN)
75+
|| (addr > (SRAM_END - len))) {
76+
return -EINVAL;
77+
}
78+
79+
/* Iterate over each section covered by the range, setting the
80+
* corresponding RAM OFF retention bit in the parent block.
81+
*/
82+
do {
83+
uintptr_t block_base = SRAM_BEGIN;
84+
uint32_t section_size = SMALL_SECTION_SIZE;
85+
uint32_t sections_per_block = SMALL_SECTIONS_PER_BLOCK;
86+
bool is_large = (addr >= LARGE_SECTION_BEGIN);
87+
uint8_t block = 0;
88+
89+
if (is_large) {
90+
block = 8;
91+
block_base = LARGE_SECTION_BEGIN;
92+
section_size = LARGE_SECTION_SIZE;
93+
94+
/* RAM[x] supports only 16 sections, each its own bit
95+
* for POWER (0..15) and RETENTION (16..31). We don't
96+
* know directly how many sections are present, so
97+
* assume they all are; the true limit will be
98+
* determined by the SRAM size.
99+
*/
100+
sections_per_block = 16;
101+
}
102+
103+
uint32_t section = (addr - block_base) / section_size;
104+
105+
if (section >= sections_per_block) {
106+
block += section / sections_per_block;
107+
section %= sections_per_block;
108+
}
109+
110+
uint32_t section_mask =
111+
(POWER_RAM_POWERSET_S0RETENTION_On
112+
<< (section + POWER_RAM_POWERSET_S0RETENTION_Pos));
113+
114+
if (enable) {
115+
nrf_power_rampower_mask_on(NRF_POWER, block, section_mask);
116+
} else {
117+
nrf_power_rampower_mask_off(NRF_POWER, block, section_mask);
118+
}
119+
120+
/* Move to the first address in the next section. */
121+
addr += section_size - (addr % section_size);
122+
} while (addr < addr_end);
123+
124+
return 0;
125+
}
126+
127+
/* Retained data must be defined in a no-init section to prevent the C
128+
* runtime initialization from zeroing it before anybody can see it.
129+
*/
130+
__noinit struct retained_data retained;
131+
132+
#define RETAINED_CRC_OFFSET offsetof(struct retained_data, crc)
133+
#define RETAINED_CHECKED_SIZE (RETAINED_CRC_OFFSET + sizeof(retained.crc))
134+
135+
bool retained_validate(void)
136+
{
137+
/* The residue of a CRC is what you get from the CRC over the
138+
* message catenated with its CRC. This is the post-final-xor
139+
* residue for CRC-32 (CRC-32/ISO-HDLC) which Zephyr calls
140+
* crc32_ieee.
141+
*/
142+
const uint32_t residue = 0x2144df1c;
143+
uint32_t crc = crc32_ieee((const uint8_t *)&retained,
144+
RETAINED_CHECKED_SIZE);
145+
bool valid = (crc == residue);
146+
147+
/* If the CRC isn't valid, reset the retained data. */
148+
if (!valid) {
149+
memset(&retained, 0, sizeof(retained));
150+
}
151+
152+
/* Reset to accrue runtime from this session. */
153+
retained.uptime_latest = 0;
154+
155+
/* Reconfigure to retain the state during system off, regardless of
156+
* whether validation succeeded. Although these values can sometimes
157+
* be observed to be preserved across System OFF, the product
158+
* specification states they are not retained in that situation, and
159+
* that can also be observed.
160+
*/
161+
(void)ram_range_retain(&retained, RETAINED_CHECKED_SIZE, true);
162+
163+
return valid;
164+
}
165+
166+
void retained_update(void)
167+
{
168+
uint64_t now = k_uptime_ticks();
169+
170+
retained.uptime_sum += (now - retained.uptime_latest);
171+
retained.uptime_latest = now;
172+
173+
uint32_t crc = crc32_ieee((const uint8_t *)&retained,
174+
RETAINED_CRC_OFFSET);
175+
176+
retained.crc = sys_cpu_to_le32(crc);
177+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2021 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef RETAINED_H_
8+
#define RETAINED_H_
9+
10+
#include <inttypes.h>
11+
12+
/* Example of validatable retained data. */
13+
struct retained_data {
14+
/* The uptime from the current session the last time the
15+
* retained data was updated.
16+
*/
17+
uint64_t uptime_latest;
18+
19+
/* Cumulative uptime from all previous sessions up through
20+
* uptime_latest of this session.
21+
*/
22+
uint64_t uptime_sum;
23+
24+
/* Number of times the application has started. */
25+
uint32_t boots;
26+
27+
/* Number of times the application has gone into system off. */
28+
uint32_t off_count;
29+
30+
/* CRC used to validate the retained data. This must be
31+
* stored little-endian, and covers everything up to but not
32+
* including this field.
33+
*/
34+
uint32_t crc;
35+
};
36+
37+
/* For simplicity in the sample just allow anybody to see and
38+
* manipulate the retained state.
39+
*/
40+
extern struct retained_data retained;
41+
42+
/* Check whether the retained data is valid, and if not reset it.
43+
*
44+
* @return true if and only if the data was valid and reflects state
45+
* from previous sessions.
46+
*/
47+
bool retained_validate(void);
48+
49+
/* Update any generic retained state and recalculate its checksum so
50+
* subsequent boots can verify the retained state.
51+
*/
52+
void retained_update(void);
53+
54+
#endif /* RETAINED_H_ */

0 commit comments

Comments
 (0)