Compare commits

...

5 Commits

Author SHA1 Message Date
569d42bbe9 Issue #18: Fix wrong documentation 2020-09-06 21:12:57 +02:00
c7ebe441c7 Merge branch 'issue/15-safety-controller-hardening' into issue/18-Backup-RAM 2020-09-06 21:10:30 +02:00
403786e0c6 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
2020-09-06 21:05:00 +02:00
192bcf01f6 Merge branch 'issue/18-Backup-RAM' into issue/15-safety-controller-hardening 2020-09-06 19:54:09 +02:00
9880c701b1 Issue #15: Introduce safety weigths 2020-09-06 19:52:44 +02:00
5 changed files with 125 additions and 39 deletions

View File

@ -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 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 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. 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 contents are protected by a `CRC Checksum`_.
@ -109,4 +109,4 @@ Therefore, the CRC calculation is fixed.
The polynomial is ``0x4C11DB7`` (Ethernet CRC32): 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 .. 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

@ -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

@ -124,10 +124,10 @@ enum config_override_entry_type {
/** /**
* @brief Weights of error flags. * @brief Weights of error flags.
*/ */
enum config_override_weight { enum config_weight {
SAFETY_MEMORY_CONFIG_WEIGTH_NONE = 0, /**< @brief This flag has no global error consequence, but might be respected by certain software modules. */ SAFETY_FLAG_CONFIG_WEIGHT_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_FLAG_CONFIG_WEIGHT_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 */ SAFETY_FLAG_CONFIG_WEIGHT_PANIC = 2, /**< @brief This flag will trigger the panic mode */
}; };
/** /**
@ -138,7 +138,7 @@ struct config_override {
union { union {
struct { struct {
uint8_t flag; uint8_t flag;
enum config_override_weight weight; enum config_weight weight;
} weight_override; } weight_override;
struct { struct {
uint8_t flag; uint8_t flag;

View File

@ -29,6 +29,7 @@
#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 <reflow-controller/safety/fault.h>
#include <stm32/stm32f4xx.h> #include <stm32/stm32f4xx.h>
@ -69,15 +70,18 @@ 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;
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) volatile struct error_flag *flag_ptr;
uint32_t end_dummy;
};
#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, .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 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 IN_SECTION(.ccm.data) 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),
@ -112,6 +116,75 @@ static volatile struct analog_mon IN_SECTION(.ccm.data) analog_mons[] = {
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) static bool error_flag_get_status(const volatile struct error_flag *flag)
{ {
if (flag->error_state == flag->error_state_inv) { if (flag->error_state == flag->error_state_inv) {
@ -147,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;
@ -178,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;
@ -283,6 +343,12 @@ void safety_controller_init()
/* Trigger panic mode! */ /* Trigger panic mode! */
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) if (found_memory_state == SAFETY_MEMORY_INIT_CORRUPTED)
safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT); safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT);
@ -354,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; static uint64_t ts = 0;
enum safety_memory_state found_state; 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(); ts = systick_get_global_tick();
/* Check the safety memory */
if (safety_memory_check()) { if (safety_memory_check()) {
safety_memory_reinit(&found_state); (void)safety_memory_reinit(&found_state);
if (found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) { if (found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) {
safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT); 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 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();
safety_controller_handle_safety_memory_check();
systick = systick_get_global_tick(); systick = systick_get_global_tick();
if (systick == last_systick) { if (systick == last_systick) {
same_systick_cnt++; same_systick_cnt++;
@ -392,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);

View File

@ -19,11 +19,10 @@
*/ */
#include <reflow-controller/safety/safety-memory.h> #include <reflow-controller/safety/safety-memory.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/crc-unit.h> #include <stm-periph/crc-unit.h>
#include <stm-periph/backup-ram.h> #include <stm-periph/backup-ram.h>
#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) static int word_to_error_memory_entry(uint32_t entry_data, struct error_memory_entry *out)
{ {
int ret = 0; int ret = 0;