From d3c4e1bffc14a5120067d23d798628d3d9ad5e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Fri, 4 Sep 2020 21:03:53 +0200 Subject: [PATCH 01/27] Issue #18: Implement driver for backup RAM --- stm-firmware/include/stm-periph/backup-ram.h | 22 ++++---- stm-firmware/stm-periph/backup-ram.c | 58 ++++++++++++++++++-- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/stm-firmware/include/stm-periph/backup-ram.h b/stm-firmware/include/stm-periph/backup-ram.h index ff23813..a2d2292 100644 --- a/stm-firmware/include/stm-periph/backup-ram.h +++ b/stm-firmware/include/stm-periph/backup-ram.h @@ -23,29 +23,31 @@ /** * @brief Init the backup ram and make it accesible */ -void backup_ram_init(); +void backup_ram_init(void); /** * @brief Disable access to the backup RAM. This saves power */ -void backup_ram_disable(); +void backup_ram_disable(void); /** - * @brief Whis function overwrites the backup RAM with 0x00 + * @brief Whis function overwrites the backup RAM with 0x00000000 */ -void backup_ram_wipe(); +void backup_ram_wipe(void); /** * @brief Read data from the backup RAM * @param addr Address offset inside memory - * @param data read 32bit data + * @param data Read data + * @param count amount of 32 bit words to read + * @return 0 if successful */ -int backup_ram_get_data(uint32_t addr, uint32_t *data); +int backup_ram_get_data(uint32_t addr, uint32_t *data, uint32_t count); /** * @brief Write data structure to backup RAM - * @param data - * @return + * @param[in] data Data to write. + * @param count Count of 32 bit words to write + * @return 0 if successful */ -int backup_ram_write_data(uint32_t addr, uint32_t data); - +int backup_ram_write_data(uint32_t addr, const uint32_t *data, uint32_t count); diff --git a/stm-firmware/stm-periph/backup-ram.c b/stm-firmware/stm-periph/backup-ram.c index b2b75a5..d2bca10 100644 --- a/stm-firmware/stm-periph/backup-ram.c +++ b/stm-firmware/stm-periph/backup-ram.c @@ -21,34 +21,82 @@ #include #include #include +#include -void backup_ram_init() +#define BACKUP_RAM_BASE BKPSRAM_BASE +#define BACKUP_RAM_SIZE 4096U +#define BACKUP_RAM_SIZE_WORDS (BACKUP_RAM_SIZE / 4U) +#define BACKUP_RAM_END_ADDR (BACKUP_RAM_BASE + BACKUP_RAM_SIZE - 1U) + +#define backup_ram ((volatile uint32_t *)BACKUP_RAM_BASE) + +#if !is_power_of_two(BACKUP_RAM_SIZE) +#error "Backup RAM size ahs to be a power of two!" +#endif + +void backup_ram_init(void) { rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_PWREN)); /* Enable access to backup RAM register set */ PWR->CR |= PWR_CR_DBP; + /* Enable the backup regulator */ + PWR->CSR |= PWR_CSR_BRE; + + /* Wait until regulator is ready */ + while (!(PWR->CSR & PWR_CSR_BRR)); + /* Enable clock for backup ram interface */ rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_BKPSRAMEN)); } -void backup_ram_disable() +void backup_ram_disable(void) { + /* Disable access to backup RAM register set */ + PWR->CR &= ~PWR_CR_DBP; rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_PWREN)); + rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_BKPSRAMEN)); } -void backup_ram_wipe() +void backup_ram_wipe(void) { + uint32_t i; + for (i = 0; i < BACKUP_RAM_SIZE_WORDS; i++) + backup_ram[i] = 0UL; } -int backup_ram_get_data(uint32_t addr, uint32_t *data) +int backup_ram_get_data(uint32_t addr, uint32_t *data, uint32_t count) { + volatile uint32_t *ptr; + if (!data) + return -1002; + if (addr >= BACKUP_RAM_SIZE_WORDS) + return -1001; + + ptr = &backup_ram[addr]; + + for (; count > 0; count--) + *(data++) = *(ptr++); + + return 0; } -int backup_ram_write_data(uint32_t addr, uint32_t data) +int backup_ram_write_data(uint32_t addr, const uint32_t *data, uint32_t count) { + volatile uint32_t *ptr; + if (!data) + return -1002; + if (addr >= BACKUP_RAM_SIZE_WORDS) + return -1001; + + ptr = &backup_ram[addr]; + + for (; count > 0; count--) + *(ptr++) = *(data++); + + return 0; } From a12648ff7a501b44a2c21acc43493260751abc0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Fri, 4 Sep 2020 21:33:54 +0200 Subject: [PATCH 02/27] Issue #18: Backup RAM: Make use of backup regulator optional --- stm-firmware/include/stm-periph/backup-ram.h | 4 +++- stm-firmware/stm-periph/backup-ram.c | 12 +++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/stm-firmware/include/stm-periph/backup-ram.h b/stm-firmware/include/stm-periph/backup-ram.h index a2d2292..3f4a470 100644 --- a/stm-firmware/include/stm-periph/backup-ram.h +++ b/stm-firmware/include/stm-periph/backup-ram.h @@ -19,11 +19,13 @@ */ #include +#include /** * @brief Init the backup ram and make it accesible + * @param use_backup_regulator Enable the Backup VBAT regulator. It will be used, when VDD is powered off */ -void backup_ram_init(void); +void backup_ram_init(bool use_backup_regulator); /** * @brief Disable access to the backup RAM. This saves power diff --git a/stm-firmware/stm-periph/backup-ram.c b/stm-firmware/stm-periph/backup-ram.c index d2bca10..62ad992 100644 --- a/stm-firmware/stm-periph/backup-ram.c +++ b/stm-firmware/stm-periph/backup-ram.c @@ -34,18 +34,20 @@ #error "Backup RAM size ahs to be a power of two!" #endif -void backup_ram_init(void) +void backup_ram_init(bool use_backup_regulator) { rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_PWREN)); /* Enable access to backup RAM register set */ PWR->CR |= PWR_CR_DBP; - /* Enable the backup regulator */ - PWR->CSR |= PWR_CSR_BRE; + if (use_backup_regulator) { + /* Enable the backup regulator */ + PWR->CSR |= PWR_CSR_BRE; - /* Wait until regulator is ready */ - while (!(PWR->CSR & PWR_CSR_BRR)); + /* Wait until regulator is ready */ + while (!(PWR->CSR & PWR_CSR_BRR)); + } /* Enable clock for backup ram interface */ rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_BKPSRAMEN)); From cb3b42aece6cf679237934df0bb9c56ee8f8379a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Fri, 4 Sep 2020 22:55:34 +0200 Subject: [PATCH 03/27] Start documentation for safety RAM. Will be implemented afterwards --- doc/source/firmware/backup-ram.rst | 60 +++++++++++++++++++ doc/source/firmware/error-handling.rst | 13 +++- doc/source/firmware/flags.rst | 18 ++++++ doc/source/firmware/safety.rst | 1 + .../reflow-controller/safety/backup-memory.h | 52 ++++++++++++++++ .../reflow-controller/safety/safety-memory.h | 50 ++++++++++++++++ stm-firmware/safety/safety-memory.c | 22 +++++++ 7 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 doc/source/firmware/backup-ram.rst create mode 100644 stm-firmware/include/reflow-controller/safety/backup-memory.h create mode 100644 stm-firmware/include/reflow-controller/safety/safety-memory.h create mode 100644 stm-firmware/safety/safety-memory.c diff --git a/doc/source/firmware/backup-ram.rst b/doc/source/firmware/backup-ram.rst new file mode 100644 index 0000000..fc4e5d3 --- /dev/null +++ b/doc/source/firmware/backup-ram.rst @@ -0,0 +1,60 @@ +.. _backup_ram: + +Safety Backup RAM +================= + +Overview +-------- + +The STM controller's backup RAM is used to store different kinds of information that shall be preserved if the controller resets. +The hardware setup is missing a separate powersupply for the controller's backup domain. Therefore, the backup RAM is cleared, when the power is cut. + +The backup RAM is used to store permanent error flags (See :ref:`safety_flags`). This ensures the flags stay present, even if a system reset is performed. The only way to clear them is by cutting the power. +Because cutting the power is a way to clear the backup RAM, no separate method for clearing the error entries in the backup RAM is defined. + +The backup RAM contents are protected by a `CRC Checksum`_. + +The backup RAM is initialized and checked after boot. If the controller starts from a powered down state, +the backup RAM is empty. This is detected by an invalid `Header`_ at the beginning of the backup RAM. If this is the case, the safety ocntoller +will create a valid backup RAM image with a `Header`_, empty `Status Flag Entries`_, an empty `Error Memory`_, and a valid `CRC Checksum`_. + +If the Header is valid during boot (verified by plausible values and correct magic numbers), the backup RAM is CRC checked. +In case of a CRC error, the Backup RAM is wiped and reinitialized. On top of that, the error flag :ref:`safety_flags_safety_mem_corrupt` is set. + +.. note:: It may be possible that future versions of the hardware include a backup RAM battery / Goldcap. In this case, a way to clear the error memory will be implemented, + because it will no longer be possible to clear the error memory by cutting the power. + On top of that, the backup memory will also contain the calibration data. + +Partitioning and Entries +------------------------ + +The backup RAM consists of multiple sections. The memory section are listed below. + +Header +~~~~~~ + +The backup memory header is located at offset address: + +.. doxygendefine:: SAFETY_MEMORY_HEADER_ADDRESS + +The header is defined by the following structure: + +.. doxygenstruct:: safety_memory_header + +The validity of the header is checked, if the magic and inverse amgic fields contain the correct values, and if the offset address pointers +have values that are located inside the error memory and are not ``0`` or the same value. + +The safety memory header magic is: + +.. doxygendefine:: SAFETY_MEMORY_MAGIC + + +Status Flag Entries +~~~~~~~~~~~~~~~~~~~ + +Error Memory +~~~~~~~~~~~~ + + +CRC Checksum +~~~~~~~~~~~~ \ No newline at end of file diff --git a/doc/source/firmware/error-handling.rst b/doc/source/firmware/error-handling.rst index 16871ae..97f5ad9 100644 --- a/doc/source/firmware/error-handling.rst +++ b/doc/source/firmware/error-handling.rst @@ -6,4 +6,15 @@ Error Handling .. _safety_panic: Panic Mode ----------- \ No newline at end of file +---------- + + +.. _safety_error_mem: + +Error memory +------------ + +Permanent errors are stored in the backup RAM of the STM. This ensures, that errors can be read even after a full system reset has occured. + +.. seealso:: :ref:`backup_ram` + diff --git a/doc/source/firmware/flags.rst b/doc/source/firmware/flags.rst index aec37d1..7012abc 100644 --- a/doc/source/firmware/flags.rst +++ b/doc/source/firmware/flags.rst @@ -72,4 +72,22 @@ ERR_FLAG_MEAS_ADC_UNSTABLE persistent self-clearing Stops PID Panic Mode ========== ============= ============= =========== no yes no no +========== ============= ============= =========== + + +.. _safety_flags_safety_mem_corrupt: + +ERR_FLAG_SAFETY_MEM_CORRUPT +--------------------------- + +``ERR_FLAG_SAFETY_MEM_CORRUPT`` is set during the initialization of the controller, in case a corrupted safety memory is encountered. +In this case the error memory is reinitialized and the flag is set in the error memory. Afer a reboot it will stay asserted until the +safety backup memory is cleared + +.. seealso:: :ref:`backup_ram` + +========== ============= ============= =========== +persistent self-clearing Stops PID Panic Mode +========== ============= ============= =========== +yes no yes no ========== ============= ============= =========== \ No newline at end of file diff --git a/doc/source/firmware/safety.rst b/doc/source/firmware/safety.rst index 2c8aac9..e741b89 100644 --- a/doc/source/firmware/safety.rst +++ b/doc/source/firmware/safety.rst @@ -16,4 +16,5 @@ which forces the output zero, but does not allow any more interaction. :maxdepth: 2 flags + backup-ram error-handling diff --git a/stm-firmware/include/reflow-controller/safety/backup-memory.h b/stm-firmware/include/reflow-controller/safety/backup-memory.h new file mode 100644 index 0000000..737c890 --- /dev/null +++ b/stm-firmware/include/reflow-controller/safety/backup-memory.h @@ -0,0 +1,52 @@ +/* Reflow Oven Controller +* +* Copyright (C) 2020 Mario Hüttel +* +* This file is part of the Reflow Oven Controller Project. +* +* The reflow oven controller is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* The Reflow Oven Control Firmware is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with the reflow oven controller project. +* If not, see . +*/ + +#ifndef __SAFETY_MEMORY_H__ +#define __SAFETY_MEMORY_H__ + +/** + * @brief Magic number to signal a valid safety memory header. + */ +#define SAFETY_MEMORY_MAGIC 0x12AA5CB7 + +/** + * @brief Offset address for the safety_memory_header. + * @note Any other value than 0UL doesn't really make sense. Therfore, this should not be changed. + */ +#define SAFETY_MEMORY_HEADER_ADDRESS 0UL + +/** + * @brief Safety memory header + */ +struct safety_memory_header { + uint32_t magic; /**< @brief Magic. Set to SAFETY_MEMORY_MAGIC */ + uint32_t boot_status_offset; /**< Offset of the safety_memory_boot_status struct (in 32 bit words)*/ + uint32_t err_memory_offset; /**< Offset of the error memory */ + uint32_t err_memory_end; /**< End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */ + uint32_t magic_i; /**< @brief Invers Magic. Set to ~SAFETY_MEMORY_MAGIC */ +}; + +struct safety_memory_boot_status { + uint32_t reboot_to_bootloader; + uint32_t code_updated; +} + + +#endif /* __SAFETY_MEMORY_H__ */ diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h new file mode 100644 index 0000000..f248547 --- /dev/null +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -0,0 +1,50 @@ +/* Reflow Oven Controller +* +* Copyright (C) 2020 Mario Hüttel +* +* This file is part of the Reflow Oven Controller Project. +* +* The reflow oven controller is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* The Reflow Oven Control Firmware is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with the reflow oven controller project. +* If not, see . +*/ + +#ifndef __WATCHDOG_H__ +#define __WATCHDOG_H__ + +#include +#include +#include + +/** + * @brief Setup the watchdog for the safety controller + * @param Prescaler to use for the 32 KHz LSI clock + * @return 0 if successful + * @note Once the watchdog is enabled, it cannot be turned off! + */ +int watchdog_setup(uint8_t prescaler); + +/** + * @brief Reset watchdog counter + * @param magic Magic value to prevent this fuinction from being called randomly + * @return 0 if successful + */ +int watchdog_ack(uint32_t magic); + +/** + * @brief Check if reset was generated by the watchdog. + * @note This also clears the relevant flag, so the function will reutrn false when called a second time + * @return + */ +bool watchdog_check_reset_source(void); + +#endif /* __WATCHDOG_H__ */ diff --git a/stm-firmware/safety/safety-memory.c b/stm-firmware/safety/safety-memory.c new file mode 100644 index 0000000..2ec93a5 --- /dev/null +++ b/stm-firmware/safety/safety-memory.c @@ -0,0 +1,22 @@ +/* Reflow Oven Controller +* +* Copyright (C) 2020 Mario Hüttel +* +* This file is part of the Reflow Oven Controller Project. +* +* The reflow oven controller is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* The Reflow Oven Control Firmware is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with the reflow oven controller project. +* If not, see . +*/ + +#include + From 5d437f3a9f27e4cdea30e6c86c3b211c62d236b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Fri, 4 Sep 2020 23:02:23 +0200 Subject: [PATCH 04/27] Fix documentation and add safety RAM module to Makefile --- doc/source/firmware/safety.rst | 2 +- stm-firmware/Makefile | 2 +- .../reflow-controller/safety/backup-memory.h | 52 ------------------- .../reflow-controller/safety/safety-memory.h | 40 +++++++------- 4 files changed, 24 insertions(+), 72 deletions(-) delete mode 100644 stm-firmware/include/reflow-controller/safety/backup-memory.h diff --git a/doc/source/firmware/safety.rst b/doc/source/firmware/safety.rst index e741b89..ba22094 100644 --- a/doc/source/firmware/safety.rst +++ b/doc/source/firmware/safety.rst @@ -10,7 +10,7 @@ Severe error flags, like a drifting reference voltage, stop the PID controller a The controller stays in a usable state. After the errors have been cleared, normal operation may continue. On the other hand, fatal errors like an over-temperature error, or memory problem, lead to the activation of the :ref:`safety_panic`, -which forces the output zero, but does not allow any more interaction. +which forces the output zero, but does not allow any further interaction. .. toctree:: :maxdepth: 2 diff --git a/stm-firmware/Makefile b/stm-firmware/Makefile index 701f726..67b2f40 100644 --- a/stm-firmware/Makefile +++ b/stm-firmware/Makefile @@ -47,7 +47,7 @@ CFILES += fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c fatfs/shi CFILES += pid-controller.c oven-driver.c CFILES += settings/settings.c settings/settings-sd-card.c -CFILES += safety/safety-adc.c safety/safety-controller.c safety/watchdog.c safety/fault.c +CFILES += safety/safety-adc.c safety/safety-controller.c safety/watchdog.c safety/fault.c safety/safety-memory.c DEBUG_DEFINES = -DDEBUGBUILD RELEASE_DEFINES = diff --git a/stm-firmware/include/reflow-controller/safety/backup-memory.h b/stm-firmware/include/reflow-controller/safety/backup-memory.h deleted file mode 100644 index 737c890..0000000 --- a/stm-firmware/include/reflow-controller/safety/backup-memory.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Reflow Oven Controller -* -* Copyright (C) 2020 Mario Hüttel -* -* This file is part of the Reflow Oven Controller Project. -* -* The reflow oven controller is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License version 2 as -* published by the Free Software Foundation. -* -* The Reflow Oven Control Firmware is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with the reflow oven controller project. -* If not, see . -*/ - -#ifndef __SAFETY_MEMORY_H__ -#define __SAFETY_MEMORY_H__ - -/** - * @brief Magic number to signal a valid safety memory header. - */ -#define SAFETY_MEMORY_MAGIC 0x12AA5CB7 - -/** - * @brief Offset address for the safety_memory_header. - * @note Any other value than 0UL doesn't really make sense. Therfore, this should not be changed. - */ -#define SAFETY_MEMORY_HEADER_ADDRESS 0UL - -/** - * @brief Safety memory header - */ -struct safety_memory_header { - uint32_t magic; /**< @brief Magic. Set to SAFETY_MEMORY_MAGIC */ - uint32_t boot_status_offset; /**< Offset of the safety_memory_boot_status struct (in 32 bit words)*/ - uint32_t err_memory_offset; /**< Offset of the error memory */ - uint32_t err_memory_end; /**< End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */ - uint32_t magic_i; /**< @brief Invers Magic. Set to ~SAFETY_MEMORY_MAGIC */ -}; - -struct safety_memory_boot_status { - uint32_t reboot_to_bootloader; - uint32_t code_updated; -} - - -#endif /* __SAFETY_MEMORY_H__ */ diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index f248547..028de14 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -18,33 +18,37 @@ * If not, see . */ -#ifndef __WATCHDOG_H__ -#define __WATCHDOG_H__ +#ifndef __SAFETY_MEMORY_H__ +#define __SAFETY_MEMORY_H__ -#include #include -#include /** - * @brief Setup the watchdog for the safety controller - * @param Prescaler to use for the 32 KHz LSI clock - * @return 0 if successful - * @note Once the watchdog is enabled, it cannot be turned off! + * @brief Magic number to signal a valid safety memory header. */ -int watchdog_setup(uint8_t prescaler); +#define SAFETY_MEMORY_MAGIC 0x12AA5CB7 /** - * @brief Reset watchdog counter - * @param magic Magic value to prevent this fuinction from being called randomly - * @return 0 if successful + * @brief Offset address for the safety_memory_header. + * @note Any other value than 0UL doesn't really make sense. Therfore, this should not be changed. */ -int watchdog_ack(uint32_t magic); +#define SAFETY_MEMORY_HEADER_ADDRESS 0UL /** - * @brief Check if reset was generated by the watchdog. - * @note This also clears the relevant flag, so the function will reutrn false when called a second time - * @return + * @brief Safety memory header */ -bool watchdog_check_reset_source(void); +struct safety_memory_header { + uint32_t magic; /**< @brief Magic. Set to @ref SAFETY_MEMORY_MAGIC */ + uint32_t boot_status_offset; /**< @brief Offset of the safety_memory_boot_status struct (in 32 bit words)*/ + uint32_t err_memory_offset; /**< @brief Offset of the error memory */ + uint32_t err_memory_end; /**< @brief End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */ + uint32_t magic_i; /**< @brief Invers Magic. Set to the bitwise inverse of @ref SAFETY_MEMORY_MAGIC */ +}; -#endif /* __WATCHDOG_H__ */ +struct safety_memory_boot_status { + uint32_t reboot_to_bootloader; + uint32_t code_updated; +}; + + +#endif /* __SAFETY_MEMORY_H__ */ From 0f0afcf3595e6f3924c00c465de66e8e38bdc639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Fri, 4 Sep 2020 23:04:27 +0200 Subject: [PATCH 05/27] Issue #18: Add safety mem corrupt error flag --- stm-firmware/include/reflow-controller/safety/safety-config.h | 1 + stm-firmware/safety/safety-controller.c | 1 + 2 files changed, 2 insertions(+) diff --git a/stm-firmware/include/reflow-controller/safety/safety-config.h b/stm-firmware/include/reflow-controller/safety/safety-config.h index 937ef02..d20ef37 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-config.h +++ b/stm-firmware/include/reflow-controller/safety/safety-config.h @@ -38,6 +38,7 @@ enum safety_flag { ERR_FLAG_UNCAL = (1<<12), ERR_FLAG_DEBUG = (1<<13), ERR_FLAG_TIMING_MAIN_LOOP = (1<<14), + ERR_FLAG_SAFETY_MEM_CORRUPT = (1<<15), }; enum timing_monitor { diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index 1bdbea4..fb05f4c 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -91,6 +91,7 @@ static volatile struct error_flag flags[] = { ERR_FLAG_ENTRY(ERR_FLAG_UNCAL, false), ERR_FLAG_ENTRY(ERR_FLAG_DEBUG, true), ERR_FLAG_ENTRY(ERR_FLAG_TIMING_MAIN_LOOP, false), + ERR_FLAG_ENTRY(ERR_FLAG_SAFETY_MEM_CORRUPT, true), }; static volatile struct timing_mon timings[] = { From 928dbfb9f30872c25e43b2725159d54ecf6fc12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Fri, 4 Sep 2020 23:51:51 +0200 Subject: [PATCH 06/27] Issue #18: Firther improve documentation --- doc/source/firmware/backup-ram.rst | 48 +++++++++++++++++-- .../reflow-controller/safety/safety-memory.h | 2 + 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/doc/source/firmware/backup-ram.rst b/doc/source/firmware/backup-ram.rst index fc4e5d3..345c151 100644 --- a/doc/source/firmware/backup-ram.rst +++ b/doc/source/firmware/backup-ram.rst @@ -16,15 +16,18 @@ The backup RAM contents are protected by a `CRC Checksum`_. The backup RAM is initialized and checked after boot. If the controller starts from a powered down state, the backup RAM is empty. This is detected by an invalid `Header`_ at the beginning of the backup RAM. If this is the case, the safety ocntoller -will create a valid backup RAM image with a `Header`_, empty `Status Flag Entries`_, an empty `Error Memory`_, and a valid `CRC Checksum`_. +will create a valid backup RAM image with a `Header`_, empty `Status Flag Entries`_, empty `Config Overrides`_, an empty `Error Memory`_, and a valid `CRC Checksum`_. -If the Header is valid during boot (verified by plausible values and correct magic numbers), the backup RAM is CRC checked. -In case of a CRC error, the Backup RAM is wiped and reinitialized. On top of that, the error flag :ref:`safety_flags_safety_mem_corrupt` is set. +If the Header is valid during boot (verified by plausible values and correct magic numbers), the backup RAM is CRC checked and the error memory is +checked for valid entries. +In case of a CRC error or invalid entries in the error memory, the Backup RAM is wiped and reinitialized. On top of that, the error flag :ref:`safety_flags_safety_mem_corrupt` is set. .. note:: It may be possible that future versions of the hardware include a backup RAM battery / Goldcap. In this case, a way to clear the error memory will be implemented, because it will no longer be possible to clear the error memory by cutting the power. On top of that, the backup memory will also contain the calibration data. +..note:: The firmware will not use the ``NOP`` entries of the error memory by default, but they will be respected by the validity checker. + Partitioning and Entries ------------------------ @@ -52,9 +55,46 @@ The safety memory header magic is: Status Flag Entries ~~~~~~~~~~~~~~~~~~~ +Config Overrides +~~~~~~~~~~~~~~~~ + +Config overrides are used to override persistance and flag weights dynamically. The safety controlelr will parse the entries on +startup. + +======================= ============ ================= ===================== ===================================== +Entry Byte 1 (LSB) Byte 2 Byte 3 Byte 4 (MSB) +======================= ============ ================= ===================== ===================================== +Weight override ``0xA2`` ``Weight`` ``Flag Number`` (LSB) ``Flag number`` (MSB) (always ``0``) +Persistance override ``0x8E`` ``Persistance`` ``Flag Number`` (LSB) ``Flag number`` (MSB) (always ``0``) +======================= ============ ================= ===================== ===================================== + + Error Memory ~~~~~~~~~~~~ +The error memory contains error entries in form of 32 bit words. The entries are coded as stated below. + +``Error Flag`` entries are used to restore error flags after boot. In theory, all flags can be set using this entry type, +however, only persistent flags are stored in the error memory by the firmware. + +``NOP`` entries have no meaning. They are used as a filler. When adding a new error memory entry, the error memory is scanned until the first ``NOP`` entry is found. +It is replaced with a valid entry. If the error memory contains a word, that is not defined below, it is considered invalid and will trigger the RAM checker on boot. +``NOP`` entries can be used to preallocate the error memory in advance. + + +======================= ============ ================= ===================== ===================================== +Entry Byte 1 (LSB) Byte 2 Byte 3 Byte 4 (MSB) +======================= ============ ================= ===================== ===================================== +Error Flag ``0x51`` ``0x00`` ``Flag Number`` (LSB) ``Flag number`` (MSB) (always ``0``) +NOP Entry ``0x22`` ``0x12`` ``0xAA`` ``0xC1`` +======================= ============ ================= ===================== ===================================== CRC Checksum -~~~~~~~~~~~~ \ No newline at end of file +~~~~~~~~~~~~ + +The CRC checksum is located after the error memory. The checksum is calculated by the internal peripheral module of the STM32F4 controller. +Therefore, the CRC calculation is fixed. + +The polynomial is ``0x4C11DB7`` (Ethernet CRC32): + +.. math:: P_{CRC}(x) = x^{32}+x^{26}+x^{23}+x^{22}+x^{16}+x^{12}+x^{11}+x^{10}+x^{8}+x^{7}+x^{5}+x^{4}+x^{2}+x+1 \ No newline at end of file diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 028de14..1f8a43a 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -40,6 +40,8 @@ struct safety_memory_header { uint32_t magic; /**< @brief Magic. Set to @ref SAFETY_MEMORY_MAGIC */ uint32_t boot_status_offset; /**< @brief Offset of the safety_memory_boot_status struct (in 32 bit words)*/ + uint32_t config_overrides_offset; /**< @brief Offset address of override entries */ + uint32_t config_overrides_len; /**< @brief Length of override entries in words */ uint32_t err_memory_offset; /**< @brief Offset of the error memory */ uint32_t err_memory_end; /**< @brief End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */ uint32_t magic_i; /**< @brief Invers Magic. Set to the bitwise inverse of @ref SAFETY_MEMORY_MAGIC */ From 04008a07c057a5c34732c7ca5ababa3973c015f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 12:17:16 +0200 Subject: [PATCH 07/27] Issue #18: Implement CRC calculation module --- doc/source/firmware/safety.rst | 2 +- stm-firmware/Makefile | 2 +- .../reflow-controller/safety/safety-memory.h | 1 - stm-firmware/include/stm-periph/crc-unit.h | 38 ++++++++++++ stm-firmware/stm-periph/crc-unit.c | 59 +++++++++++++++++++ 5 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 stm-firmware/include/stm-periph/crc-unit.h create mode 100644 stm-firmware/stm-periph/crc-unit.c diff --git a/doc/source/firmware/safety.rst b/doc/source/firmware/safety.rst index ba22094..5a7d945 100644 --- a/doc/source/firmware/safety.rst +++ b/doc/source/firmware/safety.rst @@ -13,7 +13,7 @@ On the other hand, fatal errors like an over-temperature error, or memory proble which forces the output zero, but does not allow any further interaction. .. toctree:: - :maxdepth: 2 + :maxdepth: 3 flags backup-ram diff --git a/stm-firmware/Makefile b/stm-firmware/Makefile index 67b2f40..88cab6d 100644 --- a/stm-firmware/Makefile +++ b/stm-firmware/Makefile @@ -46,7 +46,7 @@ CFILES += ui/lcd.c ui/menu.c reflow-menu.c CFILES += fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c fatfs/shimatta_sdio_driver/shimatta_sdio.c CFILES += pid-controller.c oven-driver.c CFILES += settings/settings.c settings/settings-sd-card.c - +CFILES += stm-periph/crc-unit.c CFILES += safety/safety-adc.c safety/safety-controller.c safety/watchdog.c safety/fault.c safety/safety-memory.c DEBUG_DEFINES = -DDEBUGBUILD diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 1f8a43a..cbfacd5 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -52,5 +52,4 @@ struct safety_memory_boot_status { uint32_t code_updated; }; - #endif /* __SAFETY_MEMORY_H__ */ diff --git a/stm-firmware/include/stm-periph/crc-unit.h b/stm-firmware/include/stm-periph/crc-unit.h new file mode 100644 index 0000000..b9b5158 --- /dev/null +++ b/stm-firmware/include/stm-periph/crc-unit.h @@ -0,0 +1,38 @@ +/* Reflow Oven Controller + * + * Copyright (C) 2020 Mario Hüttel + * + * This file is part of the Reflow Oven Controller Project. + * + * The reflow oven controller is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * GDSII-Converter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the reflow oven controller project. + * If not, see . + */ + +#ifndef __CRC_UNIT_H__ +#define __CRC_UNIT_H__ + +#include + +void crc_unit_init(void); + +void crc_unit_deinit(void); + +void crc_unit_reset(void); + +uint32_t crc_unit_get_crc(void); + +void crc_unit_input(uint32_t data); + +void crc_unit_input_array(const uint32_t *data, uint32_t len); + +#endif /* __CRC_UNIT_H__ */ diff --git a/stm-firmware/stm-periph/crc-unit.c b/stm-firmware/stm-periph/crc-unit.c new file mode 100644 index 0000000..672315b --- /dev/null +++ b/stm-firmware/stm-periph/crc-unit.c @@ -0,0 +1,59 @@ +/* Reflow Oven Controller + * + * Copyright (C) 2020 Mario Hüttel + * + * This file is part of the Reflow Oven Controller Project. + * + * The reflow oven controller is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * GDSII-Converter is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the reflow oven controller project. + * If not, see . + */ + +#include +#include +#include + +void crc_unit_init(void) +{ + rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_CRCEN)); + crc_unit_reset(); +} + +void crc_unit_deinit(void) +{ + rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_CRCEN)); +} + +void crc_unit_reset(void) +{ + CRC->CR = CRC_CR_RESET; +} + +uint32_t crc_unit_get_crc(void) +{ + return CRC->DR; +} + +void crc_unit_input(uint32_t data) +{ + CRC->DR = data; +} + +void crc_unit_input_array(const uint32_t *data, uint32_t len) +{ + uint32_t i; + + if (!data) + return; + for (i = 0; i < len; i++) + crc_unit_input(data[i]); +} From c9a5a2c2ffd2e15fdceca72b543e31df3fc6028d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 15:15:31 +0200 Subject: [PATCH 08/27] Issue #18: Write init of safety memory --- .../reflow-controller/safety/safety-memory.h | 55 ++++++ stm-firmware/include/stm-periph/backup-ram.h | 2 + stm-firmware/safety/safety-controller.c | 11 ++ stm-firmware/safety/safety-memory.c | 185 ++++++++++++++++++ stm-firmware/stm-periph/backup-ram.c | 5 + 5 files changed, 258 insertions(+) diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index cbfacd5..541baee 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -34,6 +34,8 @@ */ #define SAFETY_MEMORY_HEADER_ADDRESS 0UL +#define SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT 512 + /** * @brief Safety memory header */ @@ -52,4 +54,57 @@ struct safety_memory_boot_status { uint32_t code_updated; }; +enum safety_memory_state { + SAFETY_MEMORY_INIT_FRESH = 0, + SAFETY_MEMORY_INIT_CORRUPTED = 1, + SAFETY_MEMORY_INIT_VALID_MEMORY = 2, +}; + +enum safety_memory_error_entry_type { + SAFETY_MEMORY_ERR_ENTRY_FLAG = 1, + SAFETY_MEMORY_ERR_ENTRY_NOP = 2, +}; + +struct error_memory_entry { + enum safety_memory_error_entry_type type; + uint16_t flag_num; +}; + +enum config_override_entry_type { + SAFETY_MEMORY_CONFIG_OVERRIDE_WEIGHT = 1, + SAFETY_MEMORY_CONFIG_OVERRIDE_PERSISTANCE = 2, +}; + +struct config_override { + enum config_override_entry_type type; + union { + struct { + uint16_t flag; + uint8_t weight; + } weight_override; + struct { + uint16_t flag; + uint8_t persistance; + } persistance_override; + } entry; +}; + +int safety_memory_init(enum safety_memory_state *found_state); + +int safety_memory_get_boot_status(struct safety_memory_boot_status *status); + +int safety_memory_get_error_entry_count(uint32_t *count); + +int safety_memory_check(void); + +int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry); + +int safety_memory_insert_error_entry(struct error_memory_entry *entry); + +int safety_memory_insert_config_override(struct config_override *config_override); + +int safety_memory_get_config_override_count(uint32_t *count); + +int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override); + #endif /* __SAFETY_MEMORY_H__ */ diff --git a/stm-firmware/include/stm-periph/backup-ram.h b/stm-firmware/include/stm-periph/backup-ram.h index 3f4a470..2d33cd6 100644 --- a/stm-firmware/include/stm-periph/backup-ram.h +++ b/stm-firmware/include/stm-periph/backup-ram.h @@ -53,3 +53,5 @@ int backup_ram_get_data(uint32_t addr, uint32_t *data, uint32_t count); * @return 0 if successful */ int backup_ram_write_data(uint32_t addr, const uint32_t *data, uint32_t count); + +uint32_t backup_ram_get_size_in_words(void); diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index fb05f4c..18e445f 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -30,10 +30,12 @@ #include #include #include +#include #include #include #include #include +#include struct error_flag { const char *name; @@ -248,6 +250,15 @@ void safety_controller_report_analog_value(enum analog_value_monitor monitor, fl void safety_controller_init() { + enum safety_memory_state found_memory_state; + + /* Init the safety memory */ + if (safety_memory_init(&found_memory_state)) { + /* Trigger panic mode! */ + panic_mode(); + } + if (found_memory_state == SAFETY_MEMORY_INIT_CORRUPTED) + safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT); /* Init default flag states */ safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_OFF | ERR_FLAG_MEAS_ADC_UNSTABLE, diff --git a/stm-firmware/safety/safety-memory.c b/stm-firmware/safety/safety-memory.c index 2ec93a5..b376cd0 100644 --- a/stm-firmware/safety/safety-memory.c +++ b/stm-firmware/safety/safety-memory.c @@ -19,4 +19,189 @@ */ #include +#include +#include +#define wordsize_of(x) ((sizeof(x) / 4U) / ((sizeof(x) % 4U) ? 0U : 1U)) + +static enum safety_memory_state safety_memory_get_header(struct safety_memory_header *header) +{ + + int res; + enum safety_memory_state ret; + + if (!header) + return SAFETY_MEMORY_INIT_CORRUPTED; + + res = backup_ram_get_data(0UL, (uint32_t *)header, wordsize_of(struct safety_memory_header)); + if (res) + return SAFETY_MEMORY_INIT_CORRUPTED; + + /* Check magics */ + if (header->magic != SAFETY_MEMORY_MAGIC || header->magic_i != (uint32_t)(~SAFETY_MEMORY_MAGIC)) { + /* Magics invalid */ + ret = SAFETY_MEMORY_INIT_FRESH; + goto return_val; + } + + res = 0; + if (header->boot_status_offset < wordsize_of(struct safety_memory_header)) + res++; + if (header->config_overrides_offset < header->boot_status_offset + wordsize_of(struct safety_memory_boot_status)) + res++; + if (header->config_overrides_len > SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT) + res++; + if (header->err_memory_offset < header->config_overrides_offset + header->config_overrides_len) + res++; + if (header->err_memory_end >= backup_ram_get_size_in_words() || header->err_memory_end < header->err_memory_offset) + res++; + + if (res) { + /* Error detected: Write new header */ + ret = SAFETY_MEMORY_INIT_CORRUPTED; + } else { + ret = SAFETY_MEMORY_INIT_VALID_MEMORY; + } + +return_val: + return ret; +} + +static void safety_memory_write_new_header(void) +{ + struct safety_memory_header header; + + header.boot_status_offset = sizeof(struct safety_memory_header); + header.config_overrides_offset = header.boot_status_offset + sizeof(struct safety_memory_boot_status)/4; + header.err_memory_offset = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT; + header.err_memory_end = header.err_memory_offset; + + backup_ram_wipe(); + backup_ram_write_data(0UL, (uint32_t *)&header, wordsize_of(header)); +} + +static int safety_memory_check_crc() +{ + struct safety_memory_header header; + enum safety_memory_state state = safety_memory_get_header(&header); + uint32_t crc_offset; + uint32_t data; + uint32_t addr; + int res; + + if (state != SAFETY_MEMORY_INIT_VALID_MEMORY) + return -1; + + crc_offset = header.err_memory_end; + + crc_unit_reset(); + + for (addr = 0; addr < crc_offset; addr++) { + res = backup_ram_get_data(addr, &data, 1UL); + if (res) + return -2000; + crc_unit_input(data); + } + + res = backup_ram_get_data(crc_offset, &data, 1UL); + if (res) + return -2001; + + if (crc_unit_get_crc() != data) + return -3000; + else + return 0; +} + +static int safety_memory_gen_crc() +{ + struct safety_memory_header header; + uint32_t word_addr; + uint32_t data; + int res; + + if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) + return -1; + + crc_unit_reset(); + for (word_addr = 0; word_addr < header.err_memory_end; word_addr++) { + res = backup_ram_get_data(word_addr, &data, 1); + if (res) + return -2; + crc_unit_input(data); + } + + /* Write CRC */ + data = crc_unit_get_crc(); + res = backup_ram_write_data(header.err_memory_end, &data, 1UL); + if (res) + return -3; + + return 0; +} + +int safety_memory_init(enum safety_memory_state *found_state) +{ + struct safety_memory_header header; + int res; + int ret = -1; + + if (!found_state) + return -1001; + + crc_unit_init(); + backup_ram_init(true); + + *found_state = safety_memory_get_header(&header); + + switch (*found_state) { + case SAFETY_MEMORY_INIT_VALID_MEMORY: + /* Valid memory detected. Check CRC */ + res = safety_memory_check_crc(); + if (res) + *found_state = SAFETY_MEMORY_INIT_CORRUPTED; + break; + case SAFETY_MEMORY_INIT_FRESH: + safety_memory_write_new_header(); + break; + case SAFETY_MEMORY_INIT_CORRUPTED: + default: + *found_state = SAFETY_MEMORY_INIT_CORRUPTED; + safety_memory_write_new_header(); + break; + } + + /* Check if memory header was written newly */ + if (*found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) { + /* If yes, generate new CRC checksum */ + res = safety_memory_gen_crc(); + if (res) + ret = -100; + else + ret = 0; + } + + return ret; +} + +int safety_memory_get_boot_status(struct safety_memory_boot_status *status); + +int safety_memory_get_error_entry_count(uint32_t *count); + +int safety_memory_check(void) +{ + int res; + + res = safety_memory_check_crc(); + return -!!res; +} + +int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry); + +int safety_memory_insert_error_entry(struct error_memory_entry *entry); + +int safety_memory_insert_config_override(struct config_override *config_override); + +int safety_memory_get_config_override_count(uint32_t *count); + +int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override); diff --git a/stm-firmware/stm-periph/backup-ram.c b/stm-firmware/stm-periph/backup-ram.c index 62ad992..720f004 100644 --- a/stm-firmware/stm-periph/backup-ram.c +++ b/stm-firmware/stm-periph/backup-ram.c @@ -102,3 +102,8 @@ int backup_ram_write_data(uint32_t addr, const uint32_t *data, uint32_t count) return 0; } + +uint32_t backup_ram_get_size_in_words(void) +{ + return (uint32_t)BACKUP_RAM_SIZE_WORDS; +} From 7434554319f2b25332de9e2302eef07a2cccbdc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 15:56:52 +0200 Subject: [PATCH 09/27] Issue #18: Fix bugs in safety memory handling --- .../reflow-controller/safety/safety-memory.h | 4 ++- stm-firmware/safety/safety-controller.c | 18 ++++++++++++ stm-firmware/safety/safety-memory.c | 29 +++++++++++++------ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 541baee..7c62ac9 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -34,7 +34,7 @@ */ #define SAFETY_MEMORY_HEADER_ADDRESS 0UL -#define SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT 512 +#define SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT 32UL /** * @brief Safety memory header @@ -91,6 +91,8 @@ struct config_override { int safety_memory_init(enum safety_memory_state *found_state); +int safety_memory_reinit(enum safety_memory_state *found_state); + int safety_memory_get_boot_status(struct safety_memory_boot_status *status); int safety_memory_get_error_entry_count(uint32_t *count); diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index 18e445f..5e10fc4 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -328,6 +328,23 @@ static void safety_controller_handle_safety_adc() } } +static void safety_controller_handle_safety_memory_check(void) +{ + static uint64_t ts = 0; + enum safety_memory_state found_state; + + if (systick_ticks_have_passed(ts, 5000)) { + ts = systick_get_global_tick(); + + if (safety_memory_check()) { + safety_memory_reinit(&found_state); + if (found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) { + safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT); + } + } + } +} + int safety_controller_handle() { static uint64_t last_systick; @@ -338,6 +355,7 @@ int safety_controller_handle() safety_controller_check_stack(); safety_controller_handle_safety_adc(); + safety_controller_handle_safety_memory_check(); systick = systick_get_global_tick(); if (systick == last_systick) { diff --git a/stm-firmware/safety/safety-memory.c b/stm-firmware/safety/safety-memory.c index b376cd0..7421219 100644 --- a/stm-firmware/safety/safety-memory.c +++ b/stm-firmware/safety/safety-memory.c @@ -71,10 +71,13 @@ static void safety_memory_write_new_header(void) { struct safety_memory_header header; - header.boot_status_offset = sizeof(struct safety_memory_header); - header.config_overrides_offset = header.boot_status_offset + sizeof(struct safety_memory_boot_status)/4; + header.boot_status_offset = wordsize_of(struct safety_memory_header); + header.config_overrides_len = SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT; + header.config_overrides_offset = header.boot_status_offset + wordsize_of(struct safety_memory_boot_status); header.err_memory_offset = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT; header.err_memory_end = header.err_memory_offset; + header.magic = SAFETY_MEMORY_MAGIC; + header.magic_i = ~SAFETY_MEMORY_MAGIC; backup_ram_wipe(); backup_ram_write_data(0UL, (uint32_t *)&header, wordsize_of(header)); @@ -140,7 +143,7 @@ static int safety_memory_gen_crc() return 0; } -int safety_memory_init(enum safety_memory_state *found_state) +int safety_memory_reinit(enum safety_memory_state *found_state) { struct safety_memory_header header; int res; @@ -149,9 +152,6 @@ int safety_memory_init(enum safety_memory_state *found_state) if (!found_state) return -1001; - crc_unit_init(); - backup_ram_init(true); - *found_state = safety_memory_get_header(&header); switch (*found_state) { @@ -162,28 +162,39 @@ int safety_memory_init(enum safety_memory_state *found_state) *found_state = SAFETY_MEMORY_INIT_CORRUPTED; break; case SAFETY_MEMORY_INIT_FRESH: - safety_memory_write_new_header(); break; case SAFETY_MEMORY_INIT_CORRUPTED: + break; default: *found_state = SAFETY_MEMORY_INIT_CORRUPTED; - safety_memory_write_new_header(); break; } - /* Check if memory header was written newly */ + /* Check if memory header has to be written */ if (*found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) { + safety_memory_write_new_header(); /* If yes, generate new CRC checksum */ res = safety_memory_gen_crc(); if (res) ret = -100; else ret = 0; + } else { + ret = 0; } return ret; } +int safety_memory_init(enum safety_memory_state *found_state) +{ + + crc_unit_init(); + backup_ram_init(true); + + return safety_memory_reinit(found_state); +} + int safety_memory_get_boot_status(struct safety_memory_boot_status *status); int safety_memory_get_error_entry_count(uint32_t *count); From 3df0631ffc7992c85bcb06a9d2e6fd830001634f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 16:32:31 +0200 Subject: [PATCH 10/27] Issue #18: Implement writing and reading boot status structure from backup RAM --- .../reflow-controller/safety/safety-memory.h | 11 +++-- stm-firmware/safety/fault.c | 9 ++++ stm-firmware/safety/safety-memory.c | 44 ++++++++++++++++++- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 7c62ac9..8ef7531 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -51,7 +51,8 @@ struct safety_memory_header { struct safety_memory_boot_status { uint32_t reboot_to_bootloader; - uint32_t code_updated; + uint32_t code_updated; + uint32_t reset_from_panic; }; enum safety_memory_state { @@ -67,7 +68,7 @@ enum safety_memory_error_entry_type { struct error_memory_entry { enum safety_memory_error_entry_type type; - uint16_t flag_num; + uint8_t flag_num; }; enum config_override_entry_type { @@ -79,11 +80,11 @@ struct config_override { enum config_override_entry_type type; union { struct { - uint16_t flag; + uint8_t flag; uint8_t weight; } weight_override; struct { - uint16_t flag; + uint8_t flag; uint8_t persistance; } persistance_override; } entry; @@ -95,6 +96,8 @@ int safety_memory_reinit(enum safety_memory_state *found_state); int safety_memory_get_boot_status(struct safety_memory_boot_status *status); +int safety_memory_set_boot_status(const struct safety_memory_boot_status *status); + int safety_memory_get_error_entry_count(uint32_t *count); int safety_memory_check(void); diff --git a/stm-firmware/safety/fault.c b/stm-firmware/safety/fault.c index 79a743d..86d8b8b 100644 --- a/stm-firmware/safety/fault.c +++ b/stm-firmware/safety/fault.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include void HardFault_Handler(void) { @@ -36,6 +38,8 @@ void HardFault_Handler(void) void panic_mode(void) { + static struct safety_memory_boot_status IN_SECTION(.ccm.bss) boot_status; + /* Panic mode is esentially the same as a hardfault, * but it can be expected, that more functionality is still usable */ @@ -44,6 +48,11 @@ void panic_mode(void) oven_driver_apply_power_level(); /* TODO: implement panic mode */ + if (!safety_memory_get_boot_status(&boot_status)) { + boot_status.reset_from_panic = 0xFFFFFFFF; + (void)safety_memory_set_boot_status(&boot_status); + } + while (1); } diff --git a/stm-firmware/safety/safety-memory.c b/stm-firmware/safety/safety-memory.c index 7421219..0cb3239 100644 --- a/stm-firmware/safety/safety-memory.c +++ b/stm-firmware/safety/safety-memory.c @@ -195,7 +195,49 @@ int safety_memory_init(enum safety_memory_state *found_state) return safety_memory_reinit(found_state); } -int safety_memory_get_boot_status(struct safety_memory_boot_status *status); +int safety_memory_get_boot_status(struct safety_memory_boot_status *status) +{ + struct safety_memory_header header; + int res; + + if (!status) + return -1001; + + if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) { + return -2000; + } + + if (safety_memory_check_crc()) + return -2001; + + res = backup_ram_get_data(header.boot_status_offset, (uint32_t *)status, wordsize_of(*status)); + if (res) + return -3000; + return 0; +} + +int safety_memory_set_boot_status(const struct safety_memory_boot_status *status) +{ + struct safety_memory_header header; + int res; + + if (!status) + return -1001; + + if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) { + return -2000; + } + + if (safety_memory_check_crc()) + return -2001; + + res = backup_ram_write_data(header.boot_status_offset, (uint32_t *)status, wordsize_of(*status)); + res |= safety_memory_gen_crc(); + if (res) + return -3000; + + return 0; +} int safety_memory_get_error_entry_count(uint32_t *count); From e85a85d9c3d69c63521d077d80b59f84bbfc926a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 16:56:56 +0200 Subject: [PATCH 11/27] Issue #18: ommand to shell in order to test panic mode --- stm-firmware/shell.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/stm-firmware/shell.c b/stm-firmware/shell.c index 481ff28..db8ef9d 100644 --- a/stm-firmware/shell.c +++ b/stm-firmware/shell.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifndef GIT_VER #define GIT_VER "VERSION NOT SET" @@ -501,6 +502,19 @@ static shellmatta_retCode_t shell_cmd_ui_emulation(const shellmatta_handle_t han return SHELLMATTA_CONTINUE; } +static shellmatta_retCode_t shell_cmd_panic(const shellmatta_handle_t handle, const char *arguments, + uint32_t length) + +{ + (void)handle; + (void)arguments; + (void)length; + + panic_mode(); + + return SHELLMATTA_OK; +} + //typedef struct shellmatta_cmd //{ // char *cmd; /**< command name */ @@ -510,7 +524,7 @@ static shellmatta_retCode_t shell_cmd_ui_emulation(const shellmatta_handle_t han // shellmatta_cmdFct_t cmdFct; /**< pointer to the cmd callack function */ // struct shellmatta_cmd *next; /**< pointer to next command or NULL */ //} shellmatta_cmd_t; -static shellmatta_cmd_t cmd[16] = { +static shellmatta_cmd_t cmd[17] = { { .cmd = "version", .cmdAlias = "ver", @@ -637,6 +651,14 @@ static shellmatta_cmd_t cmd[16] = { .helpText = "", .usageText = "", .cmdFct = shell_cmd_ui_emulation, + .next = &cmd[16], + }, + { + .cmd = "panic", + .cmdAlias = NULL, + .helpText = "Panic Mode!", + .usageText = "", + .cmdFct = shell_cmd_panic, .next = NULL, }, }; From 77c88c69cd90c3630b57aca6e53b0ab1f2d105d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 16:57:25 +0200 Subject: [PATCH 12/27] Issue #18: Redefine error memory entries --- doc/source/firmware/backup-ram.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/firmware/backup-ram.rst b/doc/source/firmware/backup-ram.rst index 345c151..2fab0d4 100644 --- a/doc/source/firmware/backup-ram.rst +++ b/doc/source/firmware/backup-ram.rst @@ -64,8 +64,8 @@ startup. ======================= ============ ================= ===================== ===================================== Entry Byte 1 (LSB) Byte 2 Byte 3 Byte 4 (MSB) ======================= ============ ================= ===================== ===================================== -Weight override ``0xA2`` ``Weight`` ``Flag Number`` (LSB) ``Flag number`` (MSB) (always ``0``) -Persistance override ``0x8E`` ``Persistance`` ``Flag Number`` (LSB) ``Flag number`` (MSB) (always ``0``) +Weight override ``0xA2`` ``Weight`` ``Flag Number`` reserved don't care (written as 0xAA) +Persistance override ``0x8E`` ``Persistance`` ``Flag Number`` reserved don't care (written as 0xBB) ======================= ============ ================= ===================== ===================================== @@ -85,7 +85,7 @@ It is replaced with a valid entry. If the error memory contains a word, that is ======================= ============ ================= ===================== ===================================== Entry Byte 1 (LSB) Byte 2 Byte 3 Byte 4 (MSB) ======================= ============ ================= ===================== ===================================== -Error Flag ``0x51`` ``0x00`` ``Flag Number`` (LSB) ``Flag number`` (MSB) (always ``0``) +Error Flag ``0x51`` ``Flag Number`` ``COUNTER 7:0`` ``COUNTER 15:8`` NOP Entry ``0x22`` ``0x12`` ``0xAA`` ``0xC1`` ======================= ============ ================= ===================== ===================================== From ea26f56545d3479ea9fce3a6785766aae97949d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 17:37:56 +0200 Subject: [PATCH 13/27] Issue #18: Check error memory entries at safety ram init --- doc/source/firmware/backup-ram.rst | 9 +++-- .../reflow-controller/safety/safety-memory.h | 5 +++ stm-firmware/main.c | 2 -- stm-firmware/safety/safety-memory.c | 33 +++++++++++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/doc/source/firmware/backup-ram.rst b/doc/source/firmware/backup-ram.rst index 2fab0d4..f24b189 100644 --- a/doc/source/firmware/backup-ram.rst +++ b/doc/source/firmware/backup-ram.rst @@ -68,19 +68,22 @@ Weight override ``0xA2`` ``Weight`` ``Flag Number`` res Persistance override ``0x8E`` ``Persistance`` ``Flag Number`` reserved don't care (written as 0xBB) ======================= ============ ================= ===================== ===================================== +All words, not matching the table above are ignored and do not cause an error. By default the firmware fills this memory area with zeroes. Error Memory ~~~~~~~~~~~~ The error memory contains error entries in form of 32 bit words. The entries are coded as stated below. -``Error Flag`` entries are used to restore error flags after boot. In theory, all flags can be set using this entry type, -however, only persistent flags are stored in the error memory by the firmware. +``Error Flag`` entries are used to restore error flags after boot. In theory, all flags can be set using this entry type. +However, only persistent flags are stored in the error memory by the firmware. ``NOP`` entries have no meaning. They are used as a filler. When adding a new error memory entry, the error memory is scanned until the first ``NOP`` entry is found. It is replaced with a valid entry. If the error memory contains a word, that is not defined below, it is considered invalid and will trigger the RAM checker on boot. -``NOP`` entries can be used to preallocate the error memory in advance. +``NOP`` entries can be used to preallocate the error memory in advance. if the end of the error memory is reached, it is expanded by 1 word to first +the new error entry, until the backup RAM is full. After this, no further errors are stored. +If the same persistent error is triggered mutliple times, the ``COUNTER`` in the error entry is incremented. ======================= ============ ================= ===================== ===================================== Entry Byte 1 (LSB) Byte 2 Byte 3 Byte 4 (MSB) diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 8ef7531..d306b37 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -28,6 +28,11 @@ */ #define SAFETY_MEMORY_MAGIC 0x12AA5CB7 +/** + * @brief Error memory NOP entry + */ +#define SAFETY_MEMORY_NOP_ENTRY 0xC1AA1222 + /** * @brief Offset address for the safety_memory_header. * @note Any other value than 0UL doesn't really make sense. Therfore, this should not be changed. diff --git a/stm-firmware/main.c b/stm-firmware/main.c index a43e42f..93a95bd 100644 --- a/stm-firmware/main.c +++ b/stm-firmware/main.c @@ -154,14 +154,12 @@ static inline void setup_system(void) setup_nvic_priorities(); systick_setup(); - oven_driver_init(); digio_setup_default_all(); led_setup(); loudspeaker_setup(); reflow_menu_init(); - uart_gpio_config(); setup_shell_uart(&shell_uart); diff --git a/stm-firmware/safety/safety-memory.c b/stm-firmware/safety/safety-memory.c index 0cb3239..a1915b4 100644 --- a/stm-firmware/safety/safety-memory.c +++ b/stm-firmware/safety/safety-memory.c @@ -239,6 +239,35 @@ int safety_memory_set_boot_status(const struct safety_memory_boot_status *status return 0; } +static int safety_memory_check_error_entries() +{ + struct safety_memory_header header; + uint32_t addr; + uint32_t data; + int ret = 0; + int res; + + if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) { + return -2000; + } + + for (addr = header.err_memory_offset; addr < header.err_memory_end; addr++) { + res = backup_ram_get_data(addr, &data, 1UL); + if (res) + return -100; + + /* Valid flag entry */ + if ((data & 0xFF) == 0x51) + continue; + if (data == SAFETY_MEMORY_NOP_ENTRY) + continue; + + ret--; + } + + return ret; +} + int safety_memory_get_error_entry_count(uint32_t *count); int safety_memory_check(void) @@ -246,6 +275,10 @@ int safety_memory_check(void) int res; res = safety_memory_check_crc(); + if (!res) { + res |= safety_memory_check_error_entries(); + } + return -!!res; } From 325fb24ed86358ae71fd79fa90e93438cb555852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 18:02:03 +0200 Subject: [PATCH 14/27] Issue #18: Improve documentation --- doc/source/firmware/backup-ram.rst | 10 ++++++---- doc/source/firmware/safety.rst | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/source/firmware/backup-ram.rst b/doc/source/firmware/backup-ram.rst index f24b189..48b9caa 100644 --- a/doc/source/firmware/backup-ram.rst +++ b/doc/source/firmware/backup-ram.rst @@ -16,7 +16,7 @@ The backup RAM contents are protected by a `CRC Checksum`_. The backup RAM is initialized and checked after boot. If the controller starts from a powered down state, the backup RAM is empty. This is detected by an invalid `Header`_ at the beginning of the backup RAM. If this is the case, the safety ocntoller -will create a valid backup RAM image with a `Header`_, empty `Status Flag Entries`_, empty `Config Overrides`_, an empty `Error Memory`_, and a valid `CRC Checksum`_. +will create a valid backup RAM image with a `Header`_, empty `Boot Status Flag Entries`_, empty `Config Overrides`_, an empty `Error Memory`_, and a valid `CRC Checksum`_. If the Header is valid during boot (verified by plausible values and correct magic numbers), the backup RAM is CRC checked and the error memory is checked for valid entries. @@ -52,13 +52,15 @@ The safety memory header magic is: .. doxygendefine:: SAFETY_MEMORY_MAGIC -Status Flag Entries -~~~~~~~~~~~~~~~~~~~ +.. _backup_ram_boot_flags: + +Boot Status Flag Entries +~~~~~~~~~~~~~~~~~~~~~~~~ Config Overrides ~~~~~~~~~~~~~~~~ -Config overrides are used to override persistance and flag weights dynamically. The safety controlelr will parse the entries on +Config overrides are used to override persistance and flag weights dynamically. The safety controller will parse the entries on startup. ======================= ============ ================= ===================== ===================================== diff --git a/doc/source/firmware/safety.rst b/doc/source/firmware/safety.rst index 5a7d945..e6684af 100644 --- a/doc/source/firmware/safety.rst +++ b/doc/source/firmware/safety.rst @@ -12,6 +12,9 @@ The controller stays in a usable state. After the errors have been cleared, norm On the other hand, fatal errors like an over-temperature error, or memory problem, lead to the activation of the :ref:`safety_panic`, which forces the output zero, but does not allow any further interaction. +On top of this, a :ref:`backup_ram` is implemented. It stores permantent errors, which are reset at a restart. On top of that, it stores the :ref:`backup_ram_boot_flags`, +which are used to retain boot information across resets, for example to communicate with the firmware updater etc. The RAM also contains entries, that allow overrides of flag weights and persistance. + .. toctree:: :maxdepth: 3 From b8b8e19206a82084d66878cfb6aaf933d876a2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 18:03:05 +0200 Subject: [PATCH 15/27] Reflow menu: Stop rendering the menu although nothing has changed. This reduces the current consumption. --- stm-firmware/reflow-menu.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/stm-firmware/reflow-menu.c b/stm-firmware/reflow-menu.c index e058aa0..3b07dca 100644 --- a/stm-firmware/reflow-menu.c +++ b/stm-firmware/reflow-menu.c @@ -109,6 +109,7 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry static void *my_parent; static bool button_ready; static int page = 0; + static int last_page = -1; static uint32_t uptime_secs; uint32_t new_uptime_secs; uint32_t uptime_mins; @@ -121,6 +122,7 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry if (entry_type == MENU_ENTRY_FIRST_ENTER) { uptime_secs = 0ULL; page = 0; + last_page = -1; my_parent = parent; button_ready = false; menu_display_clear(menu); @@ -144,12 +146,18 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry switch (page) { case 0: + if (last_page == 0) + break; + last_page = 0; menu_lcd_output(menu, 0, LCD_SHIMATTA_STRING " Shimatta"); menu_lcd_output(menu, 1, "Oven Controller"); menu_lcd_output(menu, 2, "(c) Mario H\xF5ttel"); menu_lcd_output(menu, 3, "Page 1/5"); break; case 1: + if (last_page == 1) + break; + last_page = 1; menu_lcd_output(menu, 0, "Version Number:"); menu_lcd_outputf(menu, 1, "%.*s", LCD_CHAR_WIDTH, xstr(GIT_VER)); if (strlen(xstr(GIT_VER)) > LCD_CHAR_WIDTH) { @@ -162,12 +170,18 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry #endif break; case 2: + if (last_page == 2) + break; + last_page = 2; menu_lcd_output(menu, 0, "Compile Info"); menu_lcd_output(menu, 1, __DATE__); menu_lcd_output(menu, 2, __TIME__); menu_lcd_output(menu, 3, "Page 3/5"); break; case 3: + if (last_page == 3) + break; + last_page = 3; unique_id_get(&ser1, &ser2, &ser3); menu_lcd_outputf(menu, 0, "Serial: %08X", ser1); @@ -187,6 +201,7 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry break; default: page = 0; + last_page = -1; break; } @@ -206,7 +221,7 @@ static void reflow_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_e (void)parent; static struct menu_list list; static bool button_valid; - static bool menu_changed = true; + bool menu_changed = false; static const char * const root_entry_names[] = { "About", "Monitoring", From e96a710576d2e6841b4d6927b6fa7e2ce15a1409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 18:23:17 +0200 Subject: [PATCH 16/27] Make interrupt default handler trigger panic mode. --- stm-firmware/safety/fault.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stm-firmware/safety/fault.c b/stm-firmware/safety/fault.c index 86d8b8b..37fe8f6 100644 --- a/stm-firmware/safety/fault.c +++ b/stm-firmware/safety/fault.c @@ -36,6 +36,12 @@ void HardFault_Handler(void) while (1); } +/* Overwrite default handler. Go to panic mode */ +void __int_default_handler(void) +{ + panic_mode(); +} + void panic_mode(void) { static struct safety_memory_boot_status IN_SECTION(.ccm.bss) boot_status; From e50602611c878a5ef5525e2d1a169ab54a955303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 19:00:01 +0200 Subject: [PATCH 17/27] Issue #18: Documentation of boot status flags --- doc/source/firmware/backup-ram.rst | 7 ++++++- .../reflow-controller/safety/safety-memory.h | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/doc/source/firmware/backup-ram.rst b/doc/source/firmware/backup-ram.rst index 48b9caa..0e36c32 100644 --- a/doc/source/firmware/backup-ram.rst +++ b/doc/source/firmware/backup-ram.rst @@ -26,7 +26,7 @@ In case of a CRC error or invalid entries in the error memory, the Backup RAM is because it will no longer be possible to clear the error memory by cutting the power. On top of that, the backup memory will also contain the calibration data. -..note:: The firmware will not use the ``NOP`` entries of the error memory by default, but they will be respected by the validity checker. +.. note:: The firmware will not use the ``NOP`` entries of the error memory by default, but they will be respected by the validity checker. Partitioning and Entries ------------------------ @@ -57,6 +57,11 @@ The safety memory header magic is: Boot Status Flag Entries ~~~~~~~~~~~~~~~~~~~~~~~~ +The boot status flag entries are use to store system states over resets. +The flags are stored in memory using the follwoing structure: + +.. doxygenstruct:: safety_memory_boot_status + Config Overrides ~~~~~~~~~~~~~~~~ diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index d306b37..2d3bce9 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -55,8 +55,29 @@ struct safety_memory_header { }; struct safety_memory_boot_status { + /** + * @brief Reboot into the bootloader + * + * When this flag is set, the controller will load the bootloader to + * memory and execute it. + */ uint32_t reboot_to_bootloader; + + /** + * @brief Bootloader has updated the code + * + * This flag is set, if the firmware ahs been updated successfully + */ uint32_t code_updated; + + /** + * @brief reset_from_panic + * + * This flag is set, when entering the panic mode. + * Because the panic mode is reset by a watchdog reset, + * this flag is needed, in order to ensure, that the panic is handled correcly after + * the watchdog reset. + */ uint32_t reset_from_panic; }; From 331b04986813ef78deb1e5134ab91155dae2ac36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 19:00:57 +0200 Subject: [PATCH 18/27] Issue #18: Documentation of boot status flags --- doc/source/firmware/backup-ram.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/firmware/backup-ram.rst b/doc/source/firmware/backup-ram.rst index 0e36c32..4d0d9f2 100644 --- a/doc/source/firmware/backup-ram.rst +++ b/doc/source/firmware/backup-ram.rst @@ -62,6 +62,8 @@ The flags are stored in memory using the follwoing structure: .. doxygenstruct:: safety_memory_boot_status +Flags are evaluated active, if the corresponding word is unequal to ``0``. + Config Overrides ~~~~~~~~~~~~~~~~ From d0cf95db49b5fcbd03d67b4077646fbdc3aaee87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 20:06:13 +0200 Subject: [PATCH 19/27] Issue #18: further iomplementation of safety memory --- .../reflow-controller/safety/safety-memory.h | 1 + stm-firmware/safety/safety-memory.c | 95 ++++++++++++++++++- 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 2d3bce9..18946ac 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -95,6 +95,7 @@ enum safety_memory_error_entry_type { struct error_memory_entry { enum safety_memory_error_entry_type type; uint8_t flag_num; + uint16_t counter; }; enum config_override_entry_type { diff --git a/stm-firmware/safety/safety-memory.c b/stm-firmware/safety/safety-memory.c index a1915b4..864a8e2 100644 --- a/stm-firmware/safety/safety-memory.c +++ b/stm-firmware/safety/safety-memory.c @@ -24,6 +24,46 @@ #define wordsize_of(x) ((sizeof(x) / 4U) / ((sizeof(x) % 4U) ? 0U : 1U)) +static int word_to_error_memory_entry(uint32_t entry_data, struct error_memory_entry *out) +{ + int ret = 0; + + if (!out) + return -1002; + + if (entry_data == SAFETY_MEMORY_ERR_ENTRY_NOP) { + out->flag_num = 0U; + out->type = SAFETY_MEMORY_ERR_ENTRY_NOP; + out->counter = 0U; + } else if ((entry_data & 0xFFU) == 0x51U) { + out->flag_num = (uint8_t)((entry_data >> 8U) & 0xFFU); + out->type = SAFETY_MEMORY_ERR_ENTRY_FLAG; + out->counter = (uint16_t)((entry_data >> 16U) & 0xFFFF); + } else { + /* Invalid entry */ + ret = -1; + } + + return ret; +} + +static uint32_t error_memory_entry_to_word(const struct error_memory_entry *entry) +{ + uint32_t word = 0; + + switch (entry->type) { + case SAFETY_MEMORY_ERR_ENTRY_NOP: + word = SAFETY_MEMORY_NOP_ENTRY; + break; + case SAFETY_MEMORY_ERR_ENTRY_FLAG: + word = 0x51UL | ((uint32_t)entry->flag_num << 8U) | + ((uint32_t)entry->counter << 16U); + break; + } + + return word; +} + static enum safety_memory_state safety_memory_get_header(struct safety_memory_header *header) { @@ -156,8 +196,8 @@ int safety_memory_reinit(enum safety_memory_state *found_state) switch (*found_state) { case SAFETY_MEMORY_INIT_VALID_MEMORY: - /* Valid memory detected. Check CRC */ - res = safety_memory_check_crc(); + /* Valid memory detected. Check CRC and error entries */ + res = safety_memory_check(); if (res) *found_state = SAFETY_MEMORY_INIT_CORRUPTED; break; @@ -268,7 +308,21 @@ static int safety_memory_check_error_entries() return ret; } -int safety_memory_get_error_entry_count(uint32_t *count); +int safety_memory_get_error_entry_count(uint32_t *count) +{ + struct safety_memory_header header; + + if (!count) + return -1001; + + if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) { + return -2000; + } + + *count = header.err_memory_end - header.err_memory_offset; + + return 0; +} int safety_memory_check(void) { @@ -282,7 +336,40 @@ int safety_memory_check(void) return -!!res; } -int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry); +int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry) +{ + struct safety_memory_header header; + uint32_t err_mem_count; + int ret = -1; + int res; + uint32_t data; + + if (!entry) + return -1001; + + if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) { + return -2000; + } + + err_mem_count = header.err_memory_end - header.err_memory_offset; + if (idx < err_mem_count && err_mem_count > 0) { + res = backup_ram_get_data(header.err_memory_offset + idx, &data, 1UL); + if (res) + goto return_value; + + res = word_to_error_memory_entry(data, entry); + if (res) + goto return_value; + ret = 0; + + } else { + /* out of range */ + ret = -1001; + } + +return_value: + return ret; +} int safety_memory_insert_error_entry(struct error_memory_entry *entry); From 1f8a6347e9bd00682b30e64a157e321bf0e81556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 20:14:08 +0200 Subject: [PATCH 20/27] Issue #15: Move safety controller working pages to CCMRAM --- stm-firmware/safety/safety-controller.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index 5e10fc4..2dc3e7b 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -36,6 +36,7 @@ #include #include #include +#include struct error_flag { const char *name; @@ -77,7 +78,7 @@ struct analog_mon { #define TIM_MON_ENTRY(mon, min, max, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min_delta = (min), .max_delta = (max), .last = 0ULL, .enabled= false} #define ANA_MON_ENTRY(mon, min_value, max_value, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min = (min_value), .max = (max_value), .value = 0.0f, .valid = false} -static volatile struct error_flag flags[] = { +static volatile struct error_flag IN_SECTION(.ccm.data) flags[] = { ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_OFF, false), ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, false), ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, false), @@ -96,14 +97,14 @@ static volatile struct error_flag flags[] = { ERR_FLAG_ENTRY(ERR_FLAG_SAFETY_MEM_CORRUPT, true), }; -static volatile struct timing_mon timings[] = { +static volatile struct timing_mon IN_SECTION(.ccm.data) timings[] = { TIM_MON_ENTRY(ERR_TIMING_PID, 2, 1000, ERR_FLAG_TIMING_PID), TIM_MON_ENTRY(ERR_TIMING_MEAS_ADC, 0, 50, ERR_FLAG_TIMING_MEAS_ADC), TIM_MON_ENTRY(ERR_TIMING_SAFETY_ADC, 10, SAFETY_CONTROLLER_ADC_DELAY_MS + 1000, ERR_FLAG_SAFETY_ADC), TIM_MON_ENTRY(ERR_TIMING_MAIN_LOOP, 0, 1000, ERR_FLAG_TIMING_MAIN_LOOP), }; -static volatile struct analog_mon analog_mons[] = { +static volatile struct analog_mon IN_SECTION(.ccm.data) analog_mons[] = { ANA_MON_ENTRY(ERR_AMON_VREF, SAFETY_ADC_VREF_MVOLT - SAFETY_ADC_VREF_TOL_MVOLT, SAFETY_ADC_VREF_MVOLT + SAFETY_ADC_VREF_TOL_MVOLT, ERR_FLAG_AMON_VREF), ANA_MON_ENTRY(ERR_AMON_UC_TEMP, SAFETY_ADC_TEMP_LOW_LIM, SAFETY_ADC_TEMP_HIGH_LIM, From b2b1702670a6950fdb16728cf66cdb74f34afb31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 20:17:35 +0200 Subject: [PATCH 21/27] Issue #15: add redundant invers error flag --- stm-firmware/safety/safety-controller.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index 2dc3e7b..c2081cb 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -42,6 +42,7 @@ struct error_flag { const char *name; enum safety_flag flag; bool error_state; + bool error_state_inv; bool persistent; uint32_t key; }; @@ -74,7 +75,7 @@ struct analog_mon { #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) -#define ERR_FLAG_ENTRY(errflag, persistency) {.name=#errflag, .flag = (errflag), .error_state = false, .persistent = (persistency), .key = 0UL} +#define ERR_FLAG_ENTRY(errflag, persistency) {.name=#errflag, .flag = (errflag), .error_state = false, .error_state_inv = true, .persistent = (persistency), .key = 0UL} #define TIM_MON_ENTRY(mon, min, max, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min_delta = (min), .max_delta = (max), .last = 0ULL, .enabled= false} #define ANA_MON_ENTRY(mon, min_value, max_value, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min = (min_value), .max = (max_value), .value = 0.0f, .valid = false} From c4fe006efad963b12b63eb9e551c77626ce7c55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sat, 5 Sep 2020 20:29:21 +0200 Subject: [PATCH 22/27] Issue #15: Implement redundancy for error flags --- stm-firmware/safety/safety-controller.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index c2081cb..f38aa2e 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -112,6 +112,15 @@ static volatile struct analog_mon IN_SECTION(.ccm.data) analog_mons[] = { ERR_FLAG_AMON_UC_TEMP), }; +static bool error_flag_get_status(const volatile struct error_flag *flag) +{ + if (flag->error_state == flag->error_state_inv) { + return true; + } else { + return flag->error_state; + } +} + static volatile struct analog_mon *find_analog_mon(enum analog_value_monitor mon) { uint32_t i; @@ -185,7 +194,6 @@ static void safety_controller_process_checks() amon_state = safety_controller_get_analog_mon_value(ERR_AMON_UC_TEMP, &amon_value); if (amon_state != ANALOG_MONITOR_OK) safety_controller_report_error(ERR_FLAG_AMON_UC_TEMP); - } safety_controller_process_active_timing_mons(); @@ -204,6 +212,7 @@ int safety_controller_report_error_with_key(enum safety_flag flag, uint32_t key) for (i = 0; i < COUNT_OF(flags); i++) { if (flags[i].flag & flag) { flags[i].error_state = true; + flags[i].error_state_inv = !flags[i].error_state; flags[i].key = key; ret = 0; } @@ -436,13 +445,15 @@ int safety_controller_get_flag(enum safety_flag flag, bool *status, bool try_ack found_flag = find_error_flag(flag); if (found_flag) { - *status = found_flag->error_state; + *status = error_flag_get_status(found_flag); if (try_ack && !found_flag->persistent) { /* Flag is generally non persistent * If key is set, this function cannot remove the flag */ - if (found_flag->key == 0UL) + if (found_flag->key == 0UL) { found_flag->error_state = false; + found_flag->error_state_inv = !found_flag->error_state; + } } } @@ -467,6 +478,7 @@ int safety_controller_ack_flag_with_key(enum safety_flag flag, uint32_t key) if (found_flag) { if (!found_flag->persistent && (found_flag->key == key || !key)) { found_flag->error_state = false; + found_flag->error_state_inv = true; ret = 0; } else { ret = -2; @@ -482,7 +494,7 @@ bool safety_controller_get_flags_by_mask(enum safety_flag mask) bool ret = false; for (i = 0; i < COUNT_OF(flags); i++) { - if ((flags[i].flag & mask) && flags[i].error_state) { + if ((flags[i].flag & mask) && error_flag_get_status(&flags[i])) { ret = true; break; } @@ -557,7 +569,7 @@ int safety_controller_get_flag_by_index(uint32_t index, bool *status, enum safet if (index < COUNT_OF(flags)) { if (status) - *status = flags[index].error_state; + *status = error_flag_get_status(&flags[index]); if (flag_enum) *flag_enum = flags[index].flag; From 6232e2f330f62fbe94b8cbe5aab5c00e95489edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sun, 6 Sep 2020 01:40:10 +0200 Subject: [PATCH 23/27] Issue #18: Store permanent errors in safety backup RAM --- stm-firmware/safety/safety-controller.c | 17 ++++- stm-firmware/safety/safety-memory.c | 95 ++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 3 deletions(-) diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index f38aa2e..a27477e 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -208,13 +208,28 @@ int safety_controller_report_error_with_key(enum safety_flag flag, uint32_t key) { uint32_t i; int ret = -1; + bool old_state; + int res; + struct error_memory_entry err_mem_entry; for (i = 0; i < COUNT_OF(flags); i++) { if (flags[i].flag & flag) { + old_state = flags[i].error_state; flags[i].error_state = true; flags[i].error_state_inv = !flags[i].error_state; flags[i].key = key; - ret = 0; + + if (flags[i].persistent && !old_state) { + err_mem_entry.counter = 1; + err_mem_entry.flag_num = i; + err_mem_entry.type = SAFETY_MEMORY_ERR_ENTRY_FLAG; + res = safety_memory_insert_error_entry(&err_mem_entry); + if (res) { + ret = -12; + } + } else { + ret = 0; + } } } diff --git a/stm-firmware/safety/safety-memory.c b/stm-firmware/safety/safety-memory.c index 864a8e2..6b21b63 100644 --- a/stm-firmware/safety/safety-memory.c +++ b/stm-firmware/safety/safety-memory.c @@ -107,6 +107,11 @@ return_val: return ret; } +static void safety_memory_write_header(const struct safety_memory_header *header) +{ + backup_ram_write_data(0UL, (uint32_t *)header, wordsize_of(*header)); +} + static void safety_memory_write_new_header(void) { struct safety_memory_header header; @@ -120,7 +125,7 @@ static void safety_memory_write_new_header(void) header.magic_i = ~SAFETY_MEMORY_MAGIC; backup_ram_wipe(); - backup_ram_write_data(0UL, (uint32_t *)&header, wordsize_of(header)); + safety_memory_write_header(&header); } static int safety_memory_check_crc() @@ -371,7 +376,93 @@ return_value: return ret; } -int safety_memory_insert_error_entry(struct error_memory_entry *entry); +int safety_memory_insert_error_entry(struct error_memory_entry *entry) +{ + int res; + int ret = -0xFFFF; + uint32_t addr; + uint32_t data; + bool found; + uint32_t input_data; + struct error_memory_entry current_entry; + + struct safety_memory_header header; + + input_data = error_memory_entry_to_word(entry); + + if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) { + return -2000; + } + + if (entry->type == SAFETY_MEMORY_ERR_ENTRY_NOP) { + /* Append to end */ + if ((header.err_memory_end + 1U) < backup_ram_get_size_in_words()) { + /* Still fits in memory */ + backup_ram_write_data(header.err_memory_end, &input_data, 1UL); + header.err_memory_end++; + safety_memory_write_header(&header); + safety_memory_gen_crc(); + ret = 0; + } + } else if (entry->type == SAFETY_MEMORY_ERR_ENTRY_FLAG) { + found = false; + for (addr = header.err_memory_offset; addr < header.err_memory_end; addr++) { + res = backup_ram_get_data(addr, &data, 1UL); + if (res) { + ret = -1; + goto return_value; + } + res = word_to_error_memory_entry(data, ¤t_entry); + if (res) { + ret = -2; + goto return_value; + } + + if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_FLAG && + current_entry.flag_num == entry->flag_num) { + found = true; + break; + } + + if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_NOP) { + found = true; + break; + } + } + + if (!found) { + /* No suitable place found in memory. Append */ + + if ((addr + 1) < backup_ram_get_size_in_words()) { + backup_ram_write_data(addr, &input_data, 1UL); + header.err_memory_end++; + safety_memory_write_header(&header); + } else { + ret = -3; + goto return_value; + } + } else { + if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_NOP) { + backup_ram_write_data(addr, &input_data, 1UL); + } else { + current_entry.counter += entry->counter; + if (current_entry.counter < entry->counter) + current_entry.counter = 0xFFFF; + data = error_memory_entry_to_word(¤t_entry); + backup_ram_write_data(addr, &data, 1UL); + } + } + + safety_memory_gen_crc(); + ret = 0; + + } else { + ret = -1001; + } + +return_value: + return ret; +} int safety_memory_insert_config_override(struct config_override *config_override); From 910037a56228c1c4beb14d6530bb5d2ba285796f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sun, 6 Sep 2020 19:45:45 +0200 Subject: [PATCH 24/27] Issue #18: Write doxygen headers for safety memory --- .../reflow-controller/safety/safety-memory.h | 120 +++++++++++++++++- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 18946ac..fe214cb 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -23,6 +23,10 @@ #include +/** @addtogroup safety-memory + * @{ + */ + /** * @brief Magic number to signal a valid safety memory header. */ @@ -81,34 +85,60 @@ struct safety_memory_boot_status { uint32_t reset_from_panic; }; +/** + * @brief The state of the safety memory + * + * This is returned by certain functions in order to signal, if the header and CRC infos are valid. + */ enum safety_memory_state { - SAFETY_MEMORY_INIT_FRESH = 0, - SAFETY_MEMORY_INIT_CORRUPTED = 1, - SAFETY_MEMORY_INIT_VALID_MEMORY = 2, + SAFETY_MEMORY_INIT_FRESH = 0, /**< @brief Memory header not found */ + SAFETY_MEMORY_INIT_CORRUPTED = 1, /**< @brief Header found, but corrupt memory */ + SAFETY_MEMORY_INIT_VALID_MEMORY = 2, /**< @brief Valid header found and CRC check is valid */ }; +/** + * @brief Types of error memory entries + */ enum safety_memory_error_entry_type { - SAFETY_MEMORY_ERR_ENTRY_FLAG = 1, - SAFETY_MEMORY_ERR_ENTRY_NOP = 2, + SAFETY_MEMORY_ERR_ENTRY_FLAG = 1, /**< @brief Flag error entry. Logs a flag */ + SAFETY_MEMORY_ERR_ENTRY_NOP = 2, /**< @brief NOP entry. Has no meaning, but will be treated as a valid entry */ }; +/** + * @brief Firmware internal representation of an error memory entry. + */ struct error_memory_entry { enum safety_memory_error_entry_type type; uint8_t flag_num; uint16_t counter; }; +/** + * @brief Types of conig override entries + */ enum config_override_entry_type { SAFETY_MEMORY_CONFIG_OVERRIDE_WEIGHT = 1, SAFETY_MEMORY_CONFIG_OVERRIDE_PERSISTANCE = 2, }; +/** + * @brief Weights of error flags. + */ +enum config_override_weight { + SAFETY_MEMORY_CONFIG_WEIGTH_NONE = 0, /**< @brief This flag has no global error consequence, but might be respected by certain software modules. */ + SAFETY_MEMORY_CONFIG_WEIGTH_PID = 1, /**< @brief This flag will force a stop of the temperature PID controller */ + SAFETY_MEMORY_CONFIG_WEIGTH_PANIC = 2, /**< @brief This flag will trigger the panic mode */ +}; + +/** + * @brief representation of a config override memory entry + */ struct config_override { enum config_override_entry_type type; union { struct { uint8_t flag; - uint8_t weight; + enum config_override_weight weight; } weight_override; struct { uint8_t flag; @@ -117,26 +147,104 @@ struct config_override { } entry; }; +/** + * @brief First time init the safety memory. This requests all clocks etc. + * + * The error memory is always vlaid after this function. At least, if it returns without error. + * The \p found_state output tells the caller, in which state the memory was found. If it was uninitialized, + * or corrupted, it is completely wiped and a fresh memory structure is written. + * + * @param[out] found_state State the error memory was found in + * @return 0 if successful + * @warning Also check @ref safety_memory_reinit + */ int safety_memory_init(enum safety_memory_state *found_state); +/** + * @brief Same as @ref safety_memory_init, but without specifically requesting the clock modules. + * + * Use this, if a call to @ref safety_memory_init has already been done. + * + * @param[out] found_state State the error memory was found in + * @return 0 if successful + */ int safety_memory_reinit(enum safety_memory_state *found_state); +/** + * @brief Get the boot status structure from safety memory + * @param[out] status Status read from memory. + * @return 0 if successful + */ int safety_memory_get_boot_status(struct safety_memory_boot_status *status); +/** + * @brief Write the boot status structure to safety memory + * @param[in] status Status to write + * @return 0 if successful + */ int safety_memory_set_boot_status(const struct safety_memory_boot_status *status); +/** + * @brief Get the amout of error entries in the error memory. This also includes NOP entries. + * @param[out] count Count + * @return 0 if successful + */ int safety_memory_get_error_entry_count(uint32_t *count); +/** + * @brief Check the header and CRC of the safety memory. + * @return 0 if all checks pass + */ int safety_memory_check(void); +/** + * @brief Read an error entry from the error memory + * @param idx Index of the entry + * @param[out] entry Error entry + * @return 0 if successful + */ int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry); +/** + * @brief Insert an error entry + * + * This function inserts an error entry on the first NOP entry found in the error memory. + * If an entry is found with the same flag number, its counter is incremented by the counter value of the + * element to insert. + * + * If there are no NOPs or fitting entries in the error memory, error memory is expanded until it hits the memory + * boundary. + * + * @param entry Error entry to insert + * @returns 0 if successful, -3 if out of memory, and other negative error codes + */ int safety_memory_insert_error_entry(struct error_memory_entry *entry); +/** + * @brief Insert a config override entry at the first free location. + * + * Free locations are entries containing 0x00000000 + * + * @param config_override Config to write + * @return 0 if successful + */ int safety_memory_insert_config_override(struct config_override *config_override); +/** + * @brief Get count of config overrides + * @param[out] count Number of overrides + * @return 0 if successful + */ int safety_memory_get_config_override_count(uint32_t *count); +/** + * @brief Get a config ovveide entry + * @param idx Index of the requested entry + * @param[out] config_override READ override + * @return 0 if successful + */ int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override); #endif /* __SAFETY_MEMORY_H__ */ + +/** @} */ From 9880c701b1fb89d6065b6676eeb8c1a8d9711f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sun, 6 Sep 2020 19:52:44 +0200 Subject: [PATCH 25/27] Issue #15: Introduce safety weigths --- .../include/reflow-controller/safety/safety-memory.h | 12 ++++++++++++ stm-firmware/safety/safety-controller.c | 12 +++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 18946ac..6e360bd 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -103,6 +103,18 @@ enum config_override_entry_type { SAFETY_MEMORY_CONFIG_OVERRIDE_PERSISTANCE = 2, }; +/** + * @brief Weights of error flags. + */ +enum config_override_weight { + SAFETY_MEMORY_CONFIG_WEIGHT_NONE = 0, /**< @brief This flag has no global error consequence, but might be respected by certain software modules. */ + SAFETY_MEMORY_CONFIG_WEIGHT_PID = 1, /**< @brief This flag will force a stop of the temperature PID controller */ + SAFETY_MEMORY_CONFIG_WEIGHT_PANIC = 2, /**< @brief This flag will trigger the panic mode */ +}; + +/** + * @brief representation of a config override memory entry + */ struct config_override { enum config_override_entry_type type; union { diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index f38aa2e..8a8e5cc 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include s struct error_flag { const char *name; @@ -69,6 +69,11 @@ struct analog_mon { uint64_t timestamp; }; +struct safety_weight { + enum config_override_weight weight; + enum safety_flag flag; +}; + #ifdef COUNT_OF #undef COUNT_OF #endif @@ -78,6 +83,7 @@ struct analog_mon { #define ERR_FLAG_ENTRY(errflag, persistency) {.name=#errflag, .flag = (errflag), .error_state = false, .error_state_inv = true, .persistent = (persistency), .key = 0UL} #define TIM_MON_ENTRY(mon, min, max, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min_delta = (min), .max_delta = (max), .last = 0ULL, .enabled= false} #define ANA_MON_ENTRY(mon, min_value, max_value, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min = (min_value), .max = (max_value), .value = 0.0f, .valid = false} +#define ERR_FLAG_WEIGTH() static volatile struct error_flag IN_SECTION(.ccm.data) flags[] = { ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_OFF, false), @@ -112,6 +118,10 @@ static volatile struct analog_mon IN_SECTION(.ccm.data) analog_mons[] = { ERR_FLAG_AMON_UC_TEMP), }; +static volatile struct safety_weight IN_SECTION(.ccm.data) flag_weigths[] = { + +} + static bool error_flag_get_status(const volatile struct error_flag *flag) { if (flag->error_state == flag->error_state_inv) { From 403786e0c67885be4f6acae70020fad0fd2e29c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sun, 6 Sep 2020 21:05:00 +0200 Subject: [PATCH 26/27] Issue #15: Implement safety weight table * CRC protected flag weight table. * Currently only filled with dummy values. Has to be finished in issue #5 * Config overrides from safety memor ynot yet implemented --- .../include/helper-macros/helper-macros.h | 1 + .../reflow-controller/safety/safety-memory.h | 10 +- stm-firmware/safety/safety-controller.c | 146 +++++++++++++----- stm-firmware/safety/safety-memory.c | 3 +- 4 files changed, 118 insertions(+), 42 deletions(-) diff --git a/stm-firmware/include/helper-macros/helper-macros.h b/stm-firmware/include/helper-macros/helper-macros.h index bafdc91..eafc4b6 100644 --- a/stm-firmware/include/helper-macros/helper-macros.h +++ b/stm-firmware/include/helper-macros/helper-macros.h @@ -30,6 +30,7 @@ #define CONCAT(x,y) x##y #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) +#define wordsize_of(x) ((sizeof(x) / 4U) / ((sizeof(x) % 4U) ? 0U : 1U)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) diff --git a/stm-firmware/include/reflow-controller/safety/safety-memory.h b/stm-firmware/include/reflow-controller/safety/safety-memory.h index 4c960ce..303d8ca 100644 --- a/stm-firmware/include/reflow-controller/safety/safety-memory.h +++ b/stm-firmware/include/reflow-controller/safety/safety-memory.h @@ -124,10 +124,10 @@ enum config_override_entry_type { /** * @brief Weights of error flags. */ -enum config_override_weight { - SAFETY_MEMORY_CONFIG_WEIGHT_NONE = 0, /**< @brief This flag has no global error consequence, but might be respected by certain software modules. */ - SAFETY_MEMORY_CONFIG_WEIGHT_PID = 1, /**< @brief This flag will force a stop of the temperature PID controller */ - SAFETY_MEMORY_CONFIG_WEIGHT_PANIC = 2, /**< @brief This flag will trigger the panic mode */ +enum config_weight { + SAFETY_FLAG_CONFIG_WEIGHT_NONE = 0, /**< @brief This flag has no global error consequence, but might be respected by certain software modules. */ + SAFETY_FLAG_CONFIG_WEIGHT_PID = 1, /**< @brief This flag will force a stop of the temperature PID controller */ + SAFETY_FLAG_CONFIG_WEIGHT_PANIC = 2, /**< @brief This flag will trigger the panic mode */ }; /** @@ -138,7 +138,7 @@ struct config_override { union { struct { uint8_t flag; - enum config_override_weight weight; + enum config_weight weight; } weight_override; struct { uint8_t flag; diff --git a/stm-firmware/safety/safety-controller.c b/stm-firmware/safety/safety-controller.c index 74371d5..42396ba 100644 --- a/stm-firmware/safety/safety-controller.c +++ b/stm-firmware/safety/safety-controller.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,7 @@ #include #include #include -#include s +#include struct error_flag { const char *name; @@ -70,20 +71,17 @@ struct analog_mon { }; struct safety_weight { - enum config_override_weight weight; + uint32_t start_dummy; + enum config_weight weight; enum safety_flag flag; + volatile struct error_flag *flag_ptr; + uint32_t end_dummy; }; -#ifdef COUNT_OF -#undef COUNT_OF -#endif - -#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) - #define ERR_FLAG_ENTRY(errflag, persistency) {.name=#errflag, .flag = (errflag), .error_state = false, .error_state_inv = true, .persistent = (persistency), .key = 0UL} #define TIM_MON_ENTRY(mon, min, max, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min_delta = (min), .max_delta = (max), .last = 0ULL, .enabled= false} #define ANA_MON_ENTRY(mon, min_value, max_value, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min = (min_value), .max = (max_value), .value = 0.0f, .valid = false} -#define ERR_FLAG_WEIGTH() +#define ERR_FLAG_WEIGHT_ENTRY(_flag, _weight) {.flag = (_flag), .flag_ptr = NULL, .weight = (_weight), .start_dummy = 0x11823344, .end_dummy = 0xAABBCCFD} static volatile struct error_flag IN_SECTION(.ccm.data) flags[] = { ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_OFF, false), @@ -118,8 +116,73 @@ static volatile struct analog_mon IN_SECTION(.ccm.data) analog_mons[] = { ERR_FLAG_AMON_UC_TEMP), }; -static volatile struct safety_weight IN_SECTION(.ccm.data) flag_weigths[] = { +static const struct safety_weight default_flag_weights[] = { + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_OFF, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_OVERFLOW, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_TIMING_MEAS_ADC, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_TIMING_PID, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_AMON_UC_TEMP, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_AMON_VREF, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_STACK, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SAFETY_ADC, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SYSTICK, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_WTCHDG_FIRED, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_UNCAL, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_DEBUG, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_TIMING_MAIN_LOOP, SAFETY_FLAG_CONFIG_WEIGHT_NONE), + ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SAFETY_MEM_CORRUPT, SAFETY_FLAG_CONFIG_WEIGHT_NONE), +}; +static volatile struct safety_weight IN_SECTION(.ccm.bss) flag_weights[COUNT_OF(default_flag_weights)]; +static uint32_t IN_SECTION(.ccm.data) flag_weight_crc; + +static int flag_weight_table_crc_check(void) +{ + /* Check the flag weight table */ + crc_unit_reset(); + crc_unit_input_array((uint32_t *)flag_weights, wordsize_of(flag_weights)); + + if (crc_unit_get_crc() != flag_weight_crc) + return -1; + + return 0; +} + +static volatile struct error_flag *find_error_flag(enum safety_flag flag) +{ + uint32_t i; + volatile struct error_flag *ret = NULL; + + for (i = 0; i < COUNT_OF(flags); i++) { + if (flags[i].flag == flag) + ret = &flags[i]; + } + + return ret; +} + +/** + * @brief This function copies the safety weigths from flash ro RAM and computes the CRC + */ +static void init_safety_flag_weight_table_from_default(void) +{ + uint32_t index; + volatile struct safety_weight *current_weight; + + /* Copy the table */ + memcpy((void *)flag_weights, default_flag_weights, wordsize_of(flag_weights)); + + /* Fill in the flag pointers */ + for (index = 0; index < COUNT_OF(flag_weights); index++) { + current_weight = &flag_weights[index]; + current_weight->flag_ptr = find_error_flag(current_weight->flag); + } + + crc_unit_reset(); + crc_unit_input_array((uint32_t*)flag_weights, wordsize_of(flag_weights)); + flag_weight_crc = crc_unit_get_crc(); } static bool error_flag_get_status(const volatile struct error_flag *flag) @@ -157,19 +220,6 @@ static volatile struct timing_mon *find_timing_mon(enum timing_monitor mon) return ret; } -static volatile struct error_flag *find_error_flag(enum safety_flag flag) -{ - uint32_t i; - volatile struct error_flag *ret = NULL; - - for (i = 0; i < COUNT_OF(flags); i++) { - if (flags[i].flag == flag) - ret = &flags[i]; - } - - return ret; -} - static void safety_controller_process_active_timing_mons() { uint32_t i; @@ -188,7 +238,7 @@ static void safety_controller_process_active_timing_mons() } } -static void safety_controller_process_checks() +static void safety_controller_process_monitor_checks() { static bool startup_completed = false; enum analog_monitor_status amon_state; @@ -293,6 +343,12 @@ void safety_controller_init() /* Trigger panic mode! */ panic_mode(); } + + /* This is usually done by the safety memory already. But, since this module also uses the CRC... */ + crc_unit_init(); + + init_safety_flag_weight_table_from_default(); + if (found_memory_state == SAFETY_MEMORY_INIT_CORRUPTED) safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT); @@ -364,35 +420,39 @@ static void safety_controller_handle_safety_adc() } } -static void safety_controller_handle_safety_memory_check(void) +/** + * @brief Check the memory structures. + * @return 0 if okay, != 0 when an error was detected. PANIC mode shall be entered in this case. + */ +static int safety_controller_handle_memory_checks(void) { static uint64_t ts = 0; enum safety_memory_state found_state; + int panic_request = 0; - if (systick_ticks_have_passed(ts, 5000)) { + if (systick_ticks_have_passed(ts, 1000)) { ts = systick_get_global_tick(); + /* Check the safety memory */ if (safety_memory_check()) { - safety_memory_reinit(&found_state); + (void)safety_memory_reinit(&found_state); if (found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) { safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT); } } + + panic_request = flag_weight_table_crc_check(); } + + return panic_request; } -int safety_controller_handle() +static void safety_controller_do_systick_checking() { static uint64_t last_systick; static uint32_t same_systick_cnt = 0UL; uint64_t systick; - int ret = 0; - - safety_controller_check_stack(); - safety_controller_handle_safety_adc(); - safety_controller_handle_safety_memory_check(); - systick = systick_get_global_tick(); if (systick == last_systick) { same_systick_cnt++; @@ -402,8 +462,24 @@ int safety_controller_handle() same_systick_cnt = 0UL; } last_systick = systick; +} - safety_controller_process_checks(); +int safety_controller_handle() +{ + int panic_requested; + int ret = 0; + + safety_controller_check_stack(); + safety_controller_handle_safety_adc(); + panic_requested = safety_controller_handle_memory_checks(); + + /* Panic here. If our internal structures are broken, we cannot be sure of anything anymore */ + if (panic_requested) + panic_mode(); + + safety_controller_do_systick_checking(); + + safety_controller_process_monitor_checks(); /* TODO: Check flags for PID and HALT */ ret |= watchdog_ack(WATCHDOG_MAGIC_KEY); diff --git a/stm-firmware/safety/safety-memory.c b/stm-firmware/safety/safety-memory.c index 6b21b63..4076c73 100644 --- a/stm-firmware/safety/safety-memory.c +++ b/stm-firmware/safety/safety-memory.c @@ -19,11 +19,10 @@ */ #include +#include #include #include -#define wordsize_of(x) ((sizeof(x) / 4U) / ((sizeof(x) % 4U) ? 0U : 1U)) - static int word_to_error_memory_entry(uint32_t entry_data, struct error_memory_entry *out) { int ret = 0; From 569d42bbe9d6d743041f67ffc1e071e9cd5eebb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Sun, 6 Sep 2020 21:12:57 +0200 Subject: [PATCH 27/27] Issue #18: Fix wrong documentation --- doc/source/firmware/backup-ram.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/firmware/backup-ram.rst b/doc/source/firmware/backup-ram.rst index 4d0d9f2..87cabdc 100644 --- a/doc/source/firmware/backup-ram.rst +++ b/doc/source/firmware/backup-ram.rst @@ -9,7 +9,7 @@ Overview The STM controller's backup RAM is used to store different kinds of information that shall be preserved if the controller resets. The hardware setup is missing a separate powersupply for the controller's backup domain. Therefore, the backup RAM is cleared, when the power is cut. -The backup RAM is used to store permanent error flags (See :ref:`safety_flags`). This ensures the flags stay present, even if a system reset is performed. The only way to clear them is by cutting the power. +The backup RAM is used to store permanent error flags (See :ref:`safety_flags`). This ensures the flags that trigger hard faults / the panic mode, can be identified, although the wathcoog resets the controller. The only way to clear them is by cutting the power. Because cutting the power is a way to clear the backup RAM, no separate method for clearing the error entries in the backup RAM is defined. The backup RAM contents are protected by a `CRC Checksum`_. @@ -109,4 +109,4 @@ Therefore, the CRC calculation is fixed. The polynomial is ``0x4C11DB7`` (Ethernet CRC32): -.. math:: P_{CRC}(x) = x^{32}+x^{26}+x^{23}+x^{22}+x^{16}+x^{12}+x^{11}+x^{10}+x^{8}+x^{7}+x^{5}+x^{4}+x^{2}+x+1 \ No newline at end of file +.. math:: P_{CRC}(x) = x^{32}+x^{26}+x^{23}+x^{22}+x^{16}+x^{12}+x^{11}+x^{10}+x^{8}+x^{7}+x^{5}+x^{4}+x^{2}+x+1