From 4e3c0777fcc08dd969d3aaf4d4e3ecd925dec8c8 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 31 May 2022 20:51:08 +0200 Subject: [PATCH] sys/flash_utils: add helpers for placing variables in flash This adds a layer of convenience abstraction over classical Harvard architectures (like most AVRs) that do not map the flash memory into the data address space and modern Harvard architectures or von-Neumann architectures that do so. The motivation is to safe a lot of RAM for AVR by storing constant strings into flash. --- cpu/atmega_common/include/cpu_conf.h | 4 + cpu/avr8_common/include/flash_utils_arch.h | 60 +++++ sys/include/flash_utils.h | 255 +++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 cpu/avr8_common/include/flash_utils_arch.h create mode 100644 sys/include/flash_utils.h diff --git a/cpu/atmega_common/include/cpu_conf.h b/cpu/atmega_common/include/cpu_conf.h index 44031b54b8..639c6ab751 100644 --- a/cpu/atmega_common/include/cpu_conf.h +++ b/cpu/atmega_common/include/cpu_conf.h @@ -77,6 +77,10 @@ extern "C" { */ #define IRQ_API_INLINED (1) +#ifndef DOXYGEN +#define HAS_FLASH_UTILS_ARCH 1 +#endif + #ifdef __cplusplus } #endif diff --git a/cpu/avr8_common/include/flash_utils_arch.h b/cpu/avr8_common/include/flash_utils_arch.h new file mode 100644 index 0000000000..4992d29a1a --- /dev/null +++ b/cpu/avr8_common/include/flash_utils_arch.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_avr8_common + * @{ + * + * @file + * @brief Implementation of flash_utils + * + * @author Marian Buschsieweke + * + */ + +#ifndef FLASH_UTILS_ARCH_H +#define FLASH_UTILS_ARCH_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* this is documented in sys/include/flash_utils.h - let's not confuse + * Doxygen */ +#ifndef DOXYGEN + +#define FLASH_ATTR __flash +#define PRIsflash "S" +#define TO_FLASH(x) __extension__({static FLASH_ATTR const char __c[] = (x); &__c[0];}) +#define flash_strcmp strcmp_P +#define flash_strncmp strncmp_P +#define flash_strlen strlen_P +#define flash_strcpy strcpy_P +#define flash_strncpy strncpy_P +#define flash_printf printf_P +/* avrlibc seemingly forgot to provide fprintf(), but vfprintf() is there */ +#define flash_vprintf(fmt, arglist) vfprintf_P(stdout, fmt, arglist) +#define flash_fprintf fprintf_P +#define flash_vfprintf vfprintf_P +#define flash_snprintf snprintf_P +#define flash_vsnprintf vsnprintf_P +#define flash_puts puts_P +#define flash_memcpy memcpy_P + +#endif /* Doxygen */ + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* FLASH_UTILS_ARCH_H */ diff --git a/sys/include/flash_utils.h b/sys/include/flash_utils.h new file mode 100644 index 0000000000..dc4721f1c7 --- /dev/null +++ b/sys/include/flash_utils.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2022 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @defgroup sys_flash_utils Utility functions, macros, and types for + * read-only memory + * @ingroup sys + * + * This modules adds utility functions, macros, and functions for read-only + * memory. The goal is to hide the differences between modified architectures + * that map flash into the data address space (e.g. ARM) and those which + * doesn't (e.g. most AVR, Xtensa). + * + * # Usage + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c} + * #include "flash_utils.h" + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * No module needs to be selected, this is a header-only implementation that + * is always available. + * + * # Porting Code to Use `flash_utils` + * + * This is mainly targeting applications developers to ease developing apps + * that work well on both legacy modified Harvard architectures (e.g. ATmega) + * and modern modified Harvard architectures (e.g. ARM, ATtiny, ...) as well + * as von-Neumann machines. + * + * The intention is to limit in-tree use to a very small number of modules that + * yield the most "bang for the buck" and not leak the use of `flash_utils` + * through the API. Specifically, reverting to not using `flash_utils` should + * not be noticed by any user (unless looking at memory consumption). + * + * @{ + * + * @file + * @brief Utility functions, macros, and types for read-only memory + * @author Marian Buschsieweke + */ + +#ifndef FLASH_UTILS_H +#define FLASH_UTILS_H + +#include +#include + +#include "cpu_conf.h" +#include "kernel_defines.h" + +#if IS_ACTIVE(HAS_FLASH_UTILS_ARCH) +#include "flash_utils_arch.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(DOXYGEN) +/** + * @brief C type qualifier required to place a variable in flash + */ +#define FLASH_ATTR +/** + * @brief Format specifier for printing `FLASH CONST char *` + * + * Usage: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c} + * FLASH_ATTR const char fmt[] = "I am printing \"%" PRIsflash "\" from flash\n"; + * FLASH_ATTR const char msg[] = "message from flash"; + * flash_printf(fmt, msg); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define PRIsflash + +/** + * @brief Macro to allocate a string literal on flash and return a + * `FLASH_ATTR const char *` pointer to it + * Usage: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c} + * flash_puts(TO_FLASH("Hello world")); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define TO_FLASH(str_literal) + +/** + * @brief Like `strcmp()`, but the second string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `strcmp()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +int flash_strcmp(const char *ram, FLASH_ATTR const char *flash); + +/** + * @brief Like `strncmp()`, but the first string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `strncmp()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +int flash_strncmp(const char *ram, FLASH_ATTR const char *flash, size_t n); + +/** + * @brief Like `strlen()`, but the string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `strlen()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +size_t flash_strlen(FLASH_ATTR const char *flash); + +/** + * @brief Like `strcpy()`, but the source flash resides in flash + * + * @details This will be a zero-overhead wrapper on top of `strcpy()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +char * flash_strcpy(char *ram, FLASH_ATTR const char *flash); + +/** + * @brief Like `strncpy()`, but the source flash resides in flash + * + * @details This will be a zero-overhead wrapper on top of `strncpy()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +char * flash_strncpy(char *ram, FLASH_ATTR const char *flash, size_t n); + +/** + * @brief Like `printf()`, but the format string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `printf()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +int flash_printf(FLASH_ATTR const char *flash, ...); + +/** + * @brief Like `vprintf()`, but the format string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `vprintf()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +int flash_vprintf(FLASH_ATTR const char *flash, va_list args); + +/** + * @brief Like `fprintf()`, but the format string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `fprintf()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +int flash_fprintf(FILE *stream, FLASH_ATTR const char *flash, ...); + +/** + * @brief Like `vfprintf()`, but the format string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `vfprintf()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +int flash_vfprintf(FILE *stream, FLASH_ATTR const char *flash, va_list args); + +/** + * @brief Like `snprintf()`, but the format string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `snprintf()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +int flash_snprintf(char *buf, size_t buf_len, FLASH_ATTR const char *flash, ...); + +/** + * @brief Like `vsnprintf()`, but the format string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `vsnprintf()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +int flash_vsnprintf(char *buf, size_t buf_len, FLASH_ATTR const char *flash, + va_list args); + +/** + * @brief Like `puts()`, but the string resides in flash + * + * @details This will be a zero-overhead wrapper on top of `puts()` for + * von-Neumann architectures or Harvard architectures that also map + * their flash into the data address space. + */ +void flash_puts(FLASH_ATTR const char *flash); + +/** + * @brief Like `memcpy()`, but @p src resides in flash + * + * @param[out] dest buffer to copy into + * @param[in] src flash data to copy + * @param[in] n number of bytes to copy + * + */ +void * flash_memcpy(void *dest, FLASH_ATTR const char *src, size_t n); +#elif !IS_ACTIVE(HAS_FLASH_UTILS_ARCH) +# define FLASH_ATTR +# define PRIsflash "s" +# define ASSERT_IS_STR_LITERAL_HELPER(x) x +# define ASSERT_IS_STR_LITERAL(x) ASSERT_IS_STR_LITERAL_HELPER("" x) +# define TO_FLASH(x) ASSERT_IS_STR_LITERAL(x) +# define flash_strcmp strcmp +# define flash_strncmp strncmp +# define flash_strlen strlen +# define flash_strcpy strcpy +# define flash_strncpy strncpy +# define flash_printf printf +# define flash_vprintf vprintf +# define flash_fprintf fprintf +# define flash_vfprintf vfprintf +# define flash_snprintf snprintf +# define flash_vsnprintf vsnprintf +# define flash_puts puts +# define flash_memcpy memcpy +#endif + +/** + * @brief A convenience wrapper for `flash_puts(TO_FLASH("str literal"))` + * + * Usage: + * ``` + * FLASH_PUTS("Hello world!"); + * ``` + */ +#define FLASH_PUTS(x) flash_puts(TO_FLASH(x)) + +/** + * @brief Like @ref flash_puts but without line break + */ +static inline void flash_print_str(FLASH_ATTR const char *flash) +{ + printf("%" PRIsflash, flash); +} + +#ifdef __cplusplus +} +#endif + +#endif /* FLASH_UTILS_H */ +/** @} */