Merge branch 'issue/18-Backup-RAM' into issue/20-implement-better-stack-checking

This commit is contained in:
Mario Hüttel 2020-09-07 21:04:37 +02:00
commit 452abfdd5c
17 changed files with 1257 additions and 58 deletions

View File

@ -0,0 +1,112 @@
.. _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 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`_.
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 `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.
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
------------------------
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
.. _backup_ram_boot_flags:
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
Flags are evaluated active, if the corresponding word is unequal to ``0``.
Config Overrides
~~~~~~~~~~~~~~~~
Config overrides are used to override persistance and flag weights dynamically. The safety controller will parse the entries on
startup.
======================= ============ ================= ===================== =====================================
Entry Byte 1 (LSB) Byte 2 Byte 3 Byte 4 (MSB)
======================= ============ ================= ===================== =====================================
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)
======================= ============ ================= ===================== =====================================
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.
``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. 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)
======================= ============ ================= ===================== =====================================
Error Flag ``0x51`` ``Flag Number`` ``COUNTER 7:0`` ``COUNTER 15:8``
NOP Entry ``0x22`` ``0x12`` ``0xAA`` ``0xC1``
======================= ============ ================= ===================== =====================================
CRC Checksum
~~~~~~~~~~~~
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

View File

@ -7,3 +7,14 @@ Error Handling
Panic Mode Panic Mode
---------- ----------
.. _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`

View File

@ -73,3 +73,21 @@ persistent self-clearing Stops PID Panic Mode
========== ============= ============= =========== ========== ============= ============= ===========
no yes no no 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
========== ============= ============= ===========

View File

@ -10,10 +10,14 @@ 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. 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`, 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.
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:: .. toctree::
:maxdepth: 2 :maxdepth: 3
flags flags
backup-ram
error-handling error-handling

View File

@ -47,8 +47,8 @@ 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 += 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 += pid-controller.c oven-driver.c
CFILES += settings/settings.c settings/settings-sd-card.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 CFILES += safety/safety-adc.c safety/safety-controller.c safety/watchdog.c safety/fault.c safety/safety-memory.c
DEBUG_DEFINES = -DDEBUGBUILD DEBUG_DEFINES = -DDEBUGBUILD
RELEASE_DEFINES = RELEASE_DEFINES =

View File

@ -30,6 +30,7 @@
#define CONCAT(x,y) x##y #define CONCAT(x,y) x##y
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) #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 MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MAX(a,b) (((a) > (b)) ? (a) : (b))

View File

@ -38,6 +38,7 @@ enum safety_flag {
ERR_FLAG_UNCAL = (1<<12), ERR_FLAG_UNCAL = (1<<12),
ERR_FLAG_DEBUG = (1<<13), ERR_FLAG_DEBUG = (1<<13),
ERR_FLAG_TIMING_MAIN_LOOP = (1<<14), ERR_FLAG_TIMING_MAIN_LOOP = (1<<14),
ERR_FLAG_SAFETY_MEM_CORRUPT = (1<<15),
}; };
enum timing_monitor { enum timing_monitor {

View File

@ -0,0 +1,250 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __SAFETY_MEMORY_H__
#define __SAFETY_MEMORY_H__
#include <stdint.h>
/** @addtogroup safety-memory
* @{
*/
/**
* @brief Magic number to signal a valid safety memory header.
*/
#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.
*/
#define SAFETY_MEMORY_HEADER_ADDRESS 0UL
#define SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT 32UL
/**
* @brief Safety memory header
*/
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 */
};
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;
};
/**
* @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, /**< @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, /**< @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_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 */
};
/**
* @brief representation of a config override memory entry
*/
struct config_override {
enum config_override_entry_type type;
union {
struct {
uint8_t flag;
enum config_weight weight;
} weight_override;
struct {
uint8_t flag;
uint8_t persistance;
} persistance_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__ */
/** @} */

View File

@ -19,33 +19,39 @@
*/ */
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
/** /**
* @brief Init the backup ram and make it accesible * @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 backup_ram_init(bool use_backup_regulator);
/** /**
* @brief Disable access to the backup RAM. This saves power * @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 * @brief Read data from the backup RAM
* @param addr Address offset inside memory * @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 * @brief Write data structure to backup RAM
* @param data * @param[in] data Data to write.
* @return * @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);
uint32_t backup_ram_get_size_in_words(void);

View File

@ -0,0 +1,38 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __CRC_UNIT_H__
#define __CRC_UNIT_H__
#include <stdint.h>
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__ */

View File

@ -154,14 +154,12 @@ static inline void setup_system(void)
setup_nvic_priorities(); setup_nvic_priorities();
systick_setup(); systick_setup();
oven_driver_init(); oven_driver_init();
digio_setup_default_all(); digio_setup_default_all();
led_setup(); led_setup();
loudspeaker_setup(); loudspeaker_setup();
reflow_menu_init(); reflow_menu_init();
uart_gpio_config(); uart_gpio_config();
setup_shell_uart(&shell_uart); setup_shell_uart(&shell_uart);

View File

@ -21,6 +21,8 @@
#include <reflow-controller/oven-driver.h> #include <reflow-controller/oven-driver.h>
#include <reflow-controller/digio.h> #include <reflow-controller/digio.h>
#include <reflow-controller/safety/fault.h> #include <reflow-controller/safety/fault.h>
#include <reflow-controller/safety/safety-memory.h>
#include <helper-macros/helper-macros.h>
void HardFault_Handler(void) void HardFault_Handler(void)
{ {
@ -42,6 +44,8 @@ void __int_default_handler(void)
void panic_mode(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, /* Panic mode is esentially the same as a hardfault,
* but it can be expected, that more functionality is still usable * but it can be expected, that more functionality is still usable
*/ */
@ -50,6 +54,11 @@ void panic_mode(void)
oven_driver_apply_power_level(); oven_driver_apply_power_level();
/* TODO: implement panic mode */ /* 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); while (1);
} }

View File

@ -29,16 +29,21 @@
#include <reflow-controller/safety/safety-adc.h> #include <reflow-controller/safety/safety-adc.h>
#include <reflow-controller/stack-check.h> #include <reflow-controller/stack-check.h>
#include <helper-macros/helper-macros.h> #include <helper-macros/helper-macros.h>
#include <stm-periph/crc-unit.h>
#include <reflow-controller/systick.h> #include <reflow-controller/systick.h>
#include <reflow-controller/safety/fault.h>
#include <stm32/stm32f4xx.h> #include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h> #include <cmsis/core_cm4.h>
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <reflow-controller/safety/safety-memory.h>
#include <helper-macros/helper-macros.h>
struct error_flag { struct error_flag {
const char *name; const char *name;
enum safety_flag flag; enum safety_flag flag;
bool error_state; bool error_state;
bool error_state_inv;
bool persistent; bool persistent;
uint32_t key; uint32_t key;
}; };
@ -65,17 +70,20 @@ struct analog_mon {
uint64_t timestamp; uint64_t timestamp;
}; };
#ifdef COUNT_OF struct safety_weight {
#undef COUNT_OF uint32_t start_dummy;
#endif enum config_weight weight;
enum safety_flag flag;
volatile struct error_flag *flag_ptr;
uint32_t end_dummy;
};
#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 ERR_FLAG_ENTRY(errflag, persistency) {.name=#errflag, .flag = (errflag), .error_state = false, .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 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 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_WEIGHT_ENTRY(_flag, _weight) {.flag = (_flag), .flag_ptr = NULL, .weight = (_weight), .start_dummy = 0x11823344, .end_dummy = 0xAABBCCFD}
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_OFF, false),
ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, false), ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, false),
ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, false), ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, false),
@ -91,22 +99,101 @@ static volatile struct error_flag flags[] = {
ERR_FLAG_ENTRY(ERR_FLAG_UNCAL, false), ERR_FLAG_ENTRY(ERR_FLAG_UNCAL, false),
ERR_FLAG_ENTRY(ERR_FLAG_DEBUG, true), ERR_FLAG_ENTRY(ERR_FLAG_DEBUG, true),
ERR_FLAG_ENTRY(ERR_FLAG_TIMING_MAIN_LOOP, false), ERR_FLAG_ENTRY(ERR_FLAG_TIMING_MAIN_LOOP, false),
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_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_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_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), 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, 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), 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, ANA_MON_ENTRY(ERR_AMON_UC_TEMP, SAFETY_ADC_TEMP_LOW_LIM, SAFETY_ADC_TEMP_HIGH_LIM,
ERR_FLAG_AMON_UC_TEMP), ERR_FLAG_AMON_UC_TEMP),
}; };
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)
{
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) static volatile struct analog_mon *find_analog_mon(enum analog_value_monitor mon)
{ {
uint32_t i; uint32_t i;
@ -133,19 +220,6 @@ static volatile struct timing_mon *find_timing_mon(enum timing_monitor mon)
return ret; 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() static void safety_controller_process_active_timing_mons()
{ {
uint32_t i; uint32_t i;
@ -164,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; static bool startup_completed = false;
enum analog_monitor_status amon_state; enum analog_monitor_status amon_state;
@ -180,7 +254,6 @@ static void safety_controller_process_checks()
amon_state = safety_controller_get_analog_mon_value(ERR_AMON_UC_TEMP, &amon_value); amon_state = safety_controller_get_analog_mon_value(ERR_AMON_UC_TEMP, &amon_value);
if (amon_state != ANALOG_MONITOR_OK) if (amon_state != ANALOG_MONITOR_OK)
safety_controller_report_error(ERR_FLAG_AMON_UC_TEMP); safety_controller_report_error(ERR_FLAG_AMON_UC_TEMP);
} }
safety_controller_process_active_timing_mons(); safety_controller_process_active_timing_mons();
@ -195,14 +268,30 @@ int safety_controller_report_error_with_key(enum safety_flag flag, uint32_t key)
{ {
uint32_t i; uint32_t i;
int ret = -1; int ret = -1;
bool old_state;
int res;
struct error_memory_entry err_mem_entry;
for (i = 0; i < COUNT_OF(flags); i++) { for (i = 0; i < COUNT_OF(flags); i++) {
if (flags[i].flag & flag) { if (flags[i].flag & flag) {
old_state = flags[i].error_state;
flags[i].error_state = true; flags[i].error_state = true;
flags[i].error_state_inv = !flags[i].error_state;
flags[i].key = key; flags[i].key = key;
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; ret = 0;
} }
} }
}
return ret; return ret;
} }
@ -247,6 +336,21 @@ void safety_controller_report_analog_value(enum analog_value_monitor monitor, fl
void safety_controller_init() 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();
}
/* 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);
/* Init default flag states */ /* Init default flag states */
safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_OFF | ERR_FLAG_MEAS_ADC_UNSTABLE, safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_OFF | ERR_FLAG_MEAS_ADC_UNSTABLE,
@ -316,17 +420,39 @@ static void safety_controller_handle_safety_adc()
} }
} }
int safety_controller_handle() /**
* @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, 1000)) {
ts = systick_get_global_tick();
/* Check the safety memory */
if (safety_memory_check()) {
(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;
}
static void safety_controller_do_systick_checking()
{ {
static uint64_t last_systick; static uint64_t last_systick;
static uint32_t same_systick_cnt = 0UL; static uint32_t same_systick_cnt = 0UL;
uint64_t systick; uint64_t systick;
int ret = 0;
safety_controller_check_stack();
safety_controller_handle_safety_adc();
systick = systick_get_global_tick(); systick = systick_get_global_tick();
if (systick == last_systick) { if (systick == last_systick) {
same_systick_cnt++; same_systick_cnt++;
@ -336,8 +462,24 @@ int safety_controller_handle()
same_systick_cnt = 0UL; same_systick_cnt = 0UL;
} }
last_systick = systick; 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 */ /* TODO: Check flags for PID and HALT */
ret |= watchdog_ack(WATCHDOG_MAGIC_KEY); ret |= watchdog_ack(WATCHDOG_MAGIC_KEY);
@ -404,13 +546,15 @@ int safety_controller_get_flag(enum safety_flag flag, bool *status, bool try_ack
found_flag = find_error_flag(flag); found_flag = find_error_flag(flag);
if (found_flag) { if (found_flag) {
*status = found_flag->error_state; *status = error_flag_get_status(found_flag);
if (try_ack && !found_flag->persistent) { if (try_ack && !found_flag->persistent) {
/* Flag is generally non persistent /* Flag is generally non persistent
* If key is set, this function cannot remove the flag * 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 = false;
found_flag->error_state_inv = !found_flag->error_state;
}
} }
} }
@ -435,6 +579,7 @@ int safety_controller_ack_flag_with_key(enum safety_flag flag, uint32_t key)
if (found_flag) { if (found_flag) {
if (!found_flag->persistent && (found_flag->key == key || !key)) { if (!found_flag->persistent && (found_flag->key == key || !key)) {
found_flag->error_state = false; found_flag->error_state = false;
found_flag->error_state_inv = true;
ret = 0; ret = 0;
} else { } else {
ret = -2; ret = -2;
@ -450,7 +595,7 @@ bool safety_controller_get_flags_by_mask(enum safety_flag mask)
bool ret = false; bool ret = false;
for (i = 0; i < COUNT_OF(flags); i++) { 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; ret = true;
break; break;
} }
@ -525,7 +670,7 @@ int safety_controller_get_flag_by_index(uint32_t index, bool *status, enum safet
if (index < COUNT_OF(flags)) { if (index < COUNT_OF(flags)) {
if (status) if (status)
*status = flags[index].error_state; *status = error_flag_get_status(&flags[index]);
if (flag_enum) if (flag_enum)
*flag_enum = flags[index].flag; *flag_enum = flags[index].flag;

View File

@ -0,0 +1,470 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/safety/safety-memory.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/crc-unit.h>
#include <stm-periph/backup-ram.h>
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)
{
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_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;
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();
safety_memory_write_header(&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_reinit(enum safety_memory_state *found_state)
{
struct safety_memory_header header;
int res;
int ret = -1;
if (!found_state)
return -1001;
*found_state = safety_memory_get_header(&header);
switch (*found_state) {
case SAFETY_MEMORY_INIT_VALID_MEMORY:
/* Valid memory detected. Check CRC and error entries */
res = safety_memory_check();
if (res)
*found_state = SAFETY_MEMORY_INIT_CORRUPTED;
break;
case SAFETY_MEMORY_INIT_FRESH:
break;
case SAFETY_MEMORY_INIT_CORRUPTED:
break;
default:
*found_state = SAFETY_MEMORY_INIT_CORRUPTED;
break;
}
/* 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)
{
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;
}
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)
{
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)
{
int res;
res = safety_memory_check_crc();
if (!res) {
res |= safety_memory_check_error_entries();
}
return -!!res;
}
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)
{
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, &current_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(&current_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);
int safety_memory_get_config_override_count(uint32_t *count);
int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override);

View File

@ -38,6 +38,7 @@
#include <reflow-controller/safety/safety-controller.h> #include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/settings/settings.h> #include <reflow-controller/settings/settings.h>
#include <reflow-controller/button.h> #include <reflow-controller/button.h>
#include <reflow-controller/safety/fault.h>
#ifndef GIT_VER #ifndef GIT_VER
#define GIT_VER "VERSION NOT SET" #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; 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 //typedef struct shellmatta_cmd
//{ //{
// char *cmd; /**< command name */ // 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 */ // shellmatta_cmdFct_t cmdFct; /**< pointer to the cmd callack function */
// struct shellmatta_cmd *next; /**< pointer to next command or NULL */ // struct shellmatta_cmd *next; /**< pointer to next command or NULL */
//} shellmatta_cmd_t; //} shellmatta_cmd_t;
static shellmatta_cmd_t cmd[16] = { static shellmatta_cmd_t cmd[17] = {
{ {
.cmd = "version", .cmd = "version",
.cmdAlias = "ver", .cmdAlias = "ver",
@ -637,6 +651,14 @@ static shellmatta_cmd_t cmd[16] = {
.helpText = "", .helpText = "",
.usageText = "", .usageText = "",
.cmdFct = shell_cmd_ui_emulation, .cmdFct = shell_cmd_ui_emulation,
.next = &cmd[16],
},
{
.cmd = "panic",
.cmdAlias = NULL,
.helpText = "Panic Mode!",
.usageText = "",
.cmdFct = shell_cmd_panic,
.next = NULL, .next = NULL,
}, },
}; };

View File

@ -21,34 +21,89 @@
#include <stm-periph/backup-ram.h> #include <stm-periph/backup-ram.h>
#include <stm-periph/clock-enable-manager.h> #include <stm-periph/clock-enable-manager.h>
#include <stm32/stm32f4xx.h> #include <stm32/stm32f4xx.h>
#include <helper-macros/helper-macros.h>
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(bool use_backup_regulator)
{ {
rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_PWREN)); rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_PWREN));
/* Enable access to backup RAM register set */ /* Enable access to backup RAM register set */
PWR->CR |= PWR_CR_DBP; PWR->CR |= PWR_CR_DBP;
if (use_backup_regulator) {
/* 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 */ /* Enable clock for backup ram interface */
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_BKPSRAMEN)); 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_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;
}
uint32_t backup_ram_get_size_in_words(void)
{
return (uint32_t)BACKUP_RAM_SIZE_WORDS;
} }

View File

@ -0,0 +1,59 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stm-periph/crc-unit.h>
#include <stm-periph/clock-enable-manager.h>
#include <stm32/stm32f4xx.h>
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]);
}