From 5964163649e1f4309665dc870861729eba4e0f31 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 10 Dec 2020 13:03:40 -0500 Subject: [PATCH] Initial SleepMemory code --- ports/esp32s2/common-hal/alarm/SleepMemory.c | 62 +++++++ ports/esp32s2/common-hal/alarm/SleepMemory.h | 63 +++++++ ports/esp32s2/common-hal/alarm/__init__.c | 16 +- ports/esp32s2/common-hal/alarm/__init__.h | 6 +- py/circuitpy_defns.mk | 1 + shared-bindings/alarm/SleepMemory.c | 180 +++++++++++++++++++ shared-bindings/alarm/SleepMemory.h | 44 +++++ shared-bindings/alarm/__init__.c | 6 +- 8 files changed, 373 insertions(+), 5 deletions(-) create mode 100644 ports/esp32s2/common-hal/alarm/SleepMemory.c create mode 100644 ports/esp32s2/common-hal/alarm/SleepMemory.h create mode 100644 shared-bindings/alarm/SleepMemory.c create mode 100644 shared-bindings/alarm/SleepMemory.h diff --git a/ports/esp32s2/common-hal/alarm/SleepMemory.c b/ports/esp32s2/common-hal/alarm/SleepMemory.c new file mode 100644 index 0000000000..2579eb5fc7 --- /dev/null +++ b/ports/esp32s2/common-hal/alarm/SleepMemory.c @@ -0,0 +1,62 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 microDev + * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/runtime.h" +#include "common-hal/alarm/SleepMemory.h" + +#include "esp_sleep.h" + +void alarm_sleep_memory_reset(void) { + // Power RTC slow memory during deep sleep + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON); +} + +uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self) { + return SLEEP_MEMORY_LENGTH; +} + +bool common_hal_alarm_sleep_memory_set_bytes(alarm_sleep_memory_obj_t *self, + uint32_t start_index, uint8_t* values, uint32_t len) { + + if (start_index + len > SLEEP_MEMORY_LENGTH) { + return false; + } + + memcpy((uint8_t *) (SLEEP_MEMORY_BASE + start_index), values, len); + return true; +} + +void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, + uint32_t start_index, uint32_t len, uint8_t* values) { + + if (start_index + len > SLEEP_MEMORY_LENGTH) { + return; + } + memcpy(values, (uint8_t *) (SLEEP_MEMORY_BASE + start_index), len); +} diff --git a/ports/esp32s2/common-hal/alarm/SleepMemory.h b/ports/esp32s2/common-hal/alarm/SleepMemory.h new file mode 100644 index 0000000000..adf7b35ae2 --- /dev/null +++ b/ports/esp32s2/common-hal/alarm/SleepMemory.h @@ -0,0 +1,63 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_ESP32S2_COMMON_HAL_ALARM_SLEEPMEMORY_H +#define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_ALARM_SLEEPMEMORY_H + +#include "py/obj.h" + +#define SLEEP_MEMORY_LENGTH (8192) + +// There are several places we could store persistent data for SleepMemory: +// +// RTC registers: There are a few 32-bit registers maintained during deep sleep. +// We are already using one for saving sleep information during deep sleep. +// +// RTC Fast Memory: 8kB, also used for deep-sleep power on stub, and for heap +// during normal operation if CONFIG_ESP32S2_ALLOW_RTC_FAST_MEM_AS_HEAP is set. +// Power-on during deep sleep must be enabled. +// I experimented with using RTC Fast Memory. It seemed to work, but occasionally, +// got smashed for unknown reasons. +// Base of RTC Fast memory on the data bus is 0x3FF9E000. The address is different on the instruction bus. +// +// RTC Slow Memory: 8kB, also used for the ULP (tiny co-processor available during sleep). +// Less likely to be used by ESP-IDF. +// Since we may want to use the ULP in the future, we will use the upper half +// of Slow Memory and reserve the lower half for ULP. +// From ulp.h: +// #define RTC_SLOW_MEM ((uint32_t*) 0x50000000) /*!< RTC slow memory, 8k size */ + +// Upper half of RTC_SLOW_MEM. +#define SLEEP_MEMORY_LENGTH (4096) +#define SLEEP_MEMORY_BASE (0x50000000 + 4096) + +typedef struct { + mp_obj_base_t base; +} alarm_sleep_memory_obj_t; + +extern void alarm_sleep_memory_reset(void); + +#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_ALARM_SLEEPMEMORY_H diff --git a/ports/esp32s2/common-hal/alarm/__init__.c b/ports/esp32s2/common-hal/alarm/__init__.c index 529179200d..fae921db0b 100644 --- a/ports/esp32s2/common-hal/alarm/__init__.c +++ b/ports/esp32s2/common-hal/alarm/__init__.c @@ -1,10 +1,10 @@ - /* +/* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * - * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries - * Copyright (c) 2019 Lucian Copeland for Adafruit Industries + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 Dan Halbert for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,6 +30,7 @@ #include "py/runtime.h" #include "shared-bindings/alarm/pin/PinAlarm.h" +#include "shared-bindings/alarm/SleepMemory.h" #include "shared-bindings/alarm/time/TimeAlarm.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/wifi/__init__.h" @@ -41,8 +42,17 @@ #include "esp_sleep.h" +// Singleton instance of SleepMemory. +const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = { + .base = { + .type = &alarm_sleep_memory_type, + }, +}; + + void alarm_reset(void) { alarm_time_timealarm_reset(); + alarm_sleep_memory_reset(); esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL); } diff --git a/ports/esp32s2/common-hal/alarm/__init__.h b/ports/esp32s2/common-hal/alarm/__init__.h index 5678a0e7f1..0823ff842e 100644 --- a/ports/esp32s2/common-hal/alarm/__init__.h +++ b/ports/esp32s2/common-hal/alarm/__init__.h @@ -27,6 +27,10 @@ #ifndef MICROPY_INCLUDED_ESP32S2_COMMON_HAL_ALARM__INIT__H #define MICROPY_INCLUDED_ESP32S2_COMMON_HAL_ALARM__INIT__H -void alarm_reset(void); +#include "common-hal/alarm/SleepMemory.h" + +const alarm_sleep_memory_obj_t alarm_sleep_memory_obj; + +extern void alarm_reset(void); #endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_ALARM__INIT__H diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index e9253682e0..1eafce2595 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -304,6 +304,7 @@ SRC_COMMON_HAL_ALL = \ _bleio/__init__.c \ _pew/PewPew.c \ _pew/__init__.c \ + alarm/SleepMemory.c \ alarm/__init__.c \ alarm/pin/PinAlarm.c \ alarm/time/TimeAlarm.c \ diff --git a/shared-bindings/alarm/SleepMemory.c b/shared-bindings/alarm/SleepMemory.c new file mode 100644 index 0000000000..d4a6eb5090 --- /dev/null +++ b/shared-bindings/alarm/SleepMemory.c @@ -0,0 +1,180 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/runtime0.h" + +#include "shared-bindings/alarm/SleepMemory.h" +#include "supervisor/shared/translate.h" + +//| class SleepMemory: +//| """Store raw bytes in RAM that persists during deep sleep. +//| The class acts as a ``bytearray``. +//| If power is lost, the memory contents are lost. +//| +//| Note that this class can't be imported and used directly. The sole +//| instance of :class:`SleepMemory` is available at +//| :attr:`alarm.sleep_memory`. +//| +//| Usage:: +//| +//| import alarm +//| alarm.sleep_memory[0] = True +//| alarm.sleep_memory[1] = 12 +//| + +//| def __init__(self) -> None: +//| """Not currently dynamically supported. Access the sole instance through `microcontroller.nvm`.""" +//| ... +//| + +//| def __bool__(self) -> bool: +//| """``sleep_memory`` is ``True`` if its length is greater than zero. +//| This is an easy way to check for its existence. +//| """ +//| ... +//| +//| def __len__(self) -> int: +//| """Return the length. This is used by (`len`)""" +//| ... +//| +STATIC mp_obj_t alarm_sleep_memory_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + alarm_sleep_memory_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint16_t len = common_hal_alarm_sleep_memory_get_length(self); + switch (op) { + case MP_UNARY_OP_BOOL: return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(len); + default: return MP_OBJ_NULL; // op not supported + } +} + +STATIC const mp_rom_map_elem_t alarm_sleep_memory_locals_dict_table[] = { +}; + +STATIC MP_DEFINE_CONST_DICT(alarm_sleep_memory_locals_dict, alarm_sleep_memory_locals_dict_table); + +//| @overload +//| def __getitem__(self, index: slice) -> bytearray: ... +//| @overload +//| def __getitem__(self, index: int) -> int: +//| """Returns the value at the given index.""" +//| ... +//| +//| @overload +//| def __setitem__(self, index: slice, value: ReadableBuffer) -> None: ... +//| @overload +//| def __setitem__(self, index: int, value: int) -> None: +//| """Set the value at the given index.""" +//| ... +//| +STATIC mp_obj_t alarm_sleep_memory_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete item + // slice deletion + return MP_OBJ_NULL; // op not supported + } else { + alarm_sleep_memory_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (0) { +#if MICROPY_PY_BUILTINS_SLICE + } else if (MP_OBJ_IS_TYPE(index_in, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(common_hal_alarm_sleep_memory_get_length(self), index_in, &slice)) { + mp_raise_NotImplementedError(translate("only slices with step=1 (aka None) are supported")); + } + if (value != MP_OBJ_SENTINEL) { + #if MICROPY_PY_ARRAY_SLICE_ASSIGN + // Assign + size_t src_len = slice.stop - slice.start; + uint8_t* src_items; + if (MP_OBJ_IS_TYPE(value, &mp_type_array) || + MP_OBJ_IS_TYPE(value, &mp_type_bytearray) || + MP_OBJ_IS_TYPE(value, &mp_type_memoryview) || + MP_OBJ_IS_TYPE(value, &mp_type_bytes)) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != src_len) { + mp_raise_ValueError(translate("Slice and value different lengths.")); + } + src_len = bufinfo.len; + src_items = bufinfo.buf; + if (1 != mp_binary_get_size('@', bufinfo.typecode, NULL)) { + mp_raise_ValueError(translate("Array values should be single bytes.")); + } + } else { + mp_raise_NotImplementedError(translate("array/bytes required on right side")); + } + + if (!common_hal_alarm_sleep_memory_set_bytes(self, slice.start, src_items, src_len)) { + mp_raise_RuntimeError(translate("Unable to write to nvm.")); + } + return mp_const_none; + #else + return MP_OBJ_NULL; // op not supported + #endif + } else { + // Read slice. + size_t len = slice.stop - slice.start; + uint8_t *items = m_new(uint8_t, len); + common_hal_alarm_sleep_memory_get_bytes(self, slice.start, len, items); + return mp_obj_new_bytearray_by_ref(len, items); + } +#endif + } else { + // Single index rather than slice. + size_t index = mp_get_index(self->base.type, common_hal_alarm_sleep_memory_get_length(self), + index_in, false); + if (value == MP_OBJ_SENTINEL) { + // load + uint8_t value_out; + common_hal_alarm_sleep_memory_get_bytes(self, index, 1, &value_out); + return MP_OBJ_NEW_SMALL_INT(value_out); + } else { + // store + mp_int_t byte_value = mp_obj_get_int(value); + if (byte_value > 0xff || byte_value < 0) { + mp_raise_ValueError(translate("Bytes must be between 0 and 255.")); + } + uint8_t short_value = byte_value; + if (!common_hal_alarm_sleep_memory_set_bytes(self, index, &short_value, 1)) { + mp_raise_RuntimeError(translate("Unable to write to nvm.")); + } + return mp_const_none; + } + } + } +} + +const mp_obj_type_t alarm_sleep_memory_type = { + { &mp_type_type }, + .name = MP_QSTR_SleepMemory, + .subscr = alarm_sleep_memory_subscr, + .unary_op = alarm_sleep_memory_unary_op, + .print = NULL, + .locals_dict = (mp_obj_t)&alarm_sleep_memory_locals_dict, +}; diff --git a/shared-bindings/alarm/SleepMemory.h b/shared-bindings/alarm/SleepMemory.h new file mode 100644 index 0000000000..0015233e0b --- /dev/null +++ b/shared-bindings/alarm/SleepMemory.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_SLEEPMEMORY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_SLEEPMEMORY_H + +#include "common-hal/alarm/SleepMemory.h" + +extern const mp_obj_type_t alarm_sleep_memory_type; + +uint32_t common_hal_alarm_sleep_memory_get_length(alarm_sleep_memory_obj_t *self); + +bool common_hal_alarm_sleep_memory_set_bytes(alarm_sleep_memory_obj_t *self, + uint32_t start_index, uint8_t* values, uint32_t len); +// len and values are intentionally swapped to signify values is an output and +// also leverage the compiler to validate uses are expected. +void common_hal_alarm_sleep_memory_get_bytes(alarm_sleep_memory_obj_t *self, + uint32_t start_index, uint32_t len, uint8_t* values); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_ALARM_SLEEPMEMORY_H diff --git a/shared-bindings/alarm/__init__.c b/shared-bindings/alarm/__init__.c index 5420b8404b..28e34ad6aa 100644 --- a/shared-bindings/alarm/__init__.c +++ b/shared-bindings/alarm/__init__.c @@ -29,6 +29,7 @@ #include "py/runtime.h" #include "shared-bindings/alarm/__init__.h" +#include "shared-bindings/alarm/SleepMemory.h" #include "shared-bindings/alarm/pin/PinAlarm.h" #include "shared-bindings/alarm/time/TimeAlarm.h" #include "shared-bindings/supervisor/Runtime.h" @@ -177,6 +178,7 @@ STATIC const mp_obj_module_t alarm_time_module = { .globals = (mp_obj_dict_t*)&alarm_time_globals, }; +// The module table is mutable because .wake_alarm is a mutable attribute. STATIC mp_map_elem_t alarm_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_alarm) }, @@ -188,8 +190,10 @@ STATIC mp_map_elem_t alarm_module_globals_table[] = { MP_OBJ_FROM_PTR(&alarm_exit_and_deep_sleep_until_alarms_obj) }, { MP_ROM_QSTR(MP_QSTR_pin), MP_OBJ_FROM_PTR(&alarm_pin_module) }, - { MP_ROM_QSTR(MP_QSTR_time), MP_OBJ_FROM_PTR(&alarm_time_module) } + { MP_ROM_QSTR(MP_QSTR_time), MP_OBJ_FROM_PTR(&alarm_time_module) }, + { MP_ROM_QSTR(MP_QSTR_SleepMemory), MP_OBJ_FROM_PTR(&alarm_sleep_memory_type) }, + { MP_ROM_QSTR(MP_QSTR_sleep_memory), MP_OBJ_FROM_PTR(&alarm_sleep_memory_obj) }, }; STATIC MP_DEFINE_MUTABLE_DICT(alarm_module_globals, alarm_module_globals_table);