From a0666b9be89239de6f407326e3deb9f8d67302c9 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Thu, 10 Feb 2022 21:04:09 +0800 Subject: [PATCH] TWAI: FIFO overrun handling and errata workarounds This commit adds handling for FIFO overruns and adds workarounds for HW erratas on the ESP32. Closes https://github.com/espressif/esp-idf/issues/2519 Closes https://github.com/espressif/esp-idf/issues/4276 --- components/app_trace/linker.lf | 2 +- components/driver/CMakeLists.txt | 3 +- components/driver/Kconfig | 42 +++++ components/driver/include/driver/twai.h | 38 ++-- components/driver/linker.lf | 9 + components/driver/periph_ctrl.c | 1 + components/driver/twai.c | 91 +++++++--- components/soc/include/hal/twai_hal.h | 140 ++++++++++++--- .../soc/soc/esp32s2/include/soc/twai_caps.h | 1 + .../soc/src/esp32/include/hal/can_hal.h | 2 +- .../soc/src/esp32/include/hal/twai_ll.h | 136 +++++++++++++++ .../soc/src/esp32s2/include/hal/twai_ll.h | 1 + components/soc/src/hal/twai_hal.c | 11 +- components/soc/src/hal/twai_hal_iram.c | 162 ++++++++++++++---- 14 files changed, 532 insertions(+), 107 deletions(-) create mode 100644 components/driver/linker.lf diff --git a/components/app_trace/linker.lf b/components/app_trace/linker.lf index 08cd419a5b..fb9c2a7647 100644 --- a/components/app_trace/linker.lf +++ b/components/app_trace/linker.lf @@ -9,7 +9,7 @@ entries: SEGGER_SYSVIEW_Config_FreeRTOS (noflash) SEGGER_SYSVIEW_FreeRTOS (noflash) -[mapping:driver] +[mapping:app_trace_driver] archive: libdriver.a entries: if SYSVIEW_TS_SOURCE_TIMER_00 = y || SYSVIEW_TS_SOURCE_TIMER_01 = y diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 108fd5d0a4..9fbd4779ac 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -51,7 +51,8 @@ idf_component_register(SRCS "${srcs}" INCLUDE_DIRS ${includes} PRIV_INCLUDE_DIRS "include/driver" PRIV_REQUIRES efuse esp_timer esp_ipc - REQUIRES esp_ringbuf freertos soc) #cannot totally hide soc headers, since there are a lot arguments in the driver are chip-dependent + REQUIRES esp_ringbuf freertos soc #cannot totally hide soc headers, since there are a lot arguments in the driver are chip-dependent + LDFRAGMENTS linker.lf) # uses C11 atomic feature set_source_files_properties(spi_master.c PROPERTIES COMPILE_FLAGS -std=gnu11) diff --git a/components/driver/Kconfig b/components/driver/Kconfig index 411388b082..e29eaec351 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -92,6 +92,48 @@ menu "Driver configurations" - Alert logging (i.e., setting of the TWAI_ALERT_AND_LOG flag) will have no effect. + config TWAI_ERRATA_FIX_BUS_OFF_REC + bool "Add SW workaround for REC change during bus-off" + depends on IDF_TARGET_ESP32 + default n + help + When the bus-off condition is reached, the REC should be reset to 0 and frozen (via LOM) by the + driver's ISR. However on the ESP32, there is an edge case where the REC will increase before the + driver's ISR can respond in time (e.g., due to the rapid occurrence of bus errors), thus causing the + REC to be non-zero after bus-off. A non-zero REC can prevent bus-off recovery as the bus-off recovery + condition is that both TEC and REC become 0. Enabling this option will add a workaround in the driver + to forcibly reset REC to zero on reaching bus-off. + + config TWAI_ERRATA_FIX_TX_INTR_LOST + bool "Add SW workaround for TX interrupt lost errata" + depends on IDF_TARGET_ESP32 + default n + help + On the ESP32, when a transmit interrupt occurs, and interrupt register is read on the same APB clock + cycle, the transmit interrupt could be lost. Enabling this option will add a workaround that checks the + transmit buffer status bit to recover any lost transmit interrupt. + + config TWAI_ERRATA_FIX_RX_FRAME_INVALID + bool "Add SW workaround for invalid RX frame errata" + depends on IDF_TARGET_ESP32 + default n + help + On the ESP32, when receiving a data or remote frame, if a bus error occurs in the data or CRC field, + the data of the next received frame could be invalid. Enabling this option will add a workaround that + will reset the peripheral on detection of this errata condition. Note that if a frame is transmitted on + the bus whilst the reset is ongoing, the message will not be receive by the peripheral sent on the bus + during the reset, the message will be lost. + + config TWAI_ERRATA_FIX_RX_FIFO_CORRUPT + bool "Add SW workaround for RX FIFO corruption errata" + depends on IDF_TARGET_ESP32 + default n + help + On the ESP32, when the RX FIFO overruns and the RX message counter maxes out at 64 messages, the entire + RX FIFO is no longer recoverable. Enabling this option will add a workaround that resets the peripheral + on detection of this errata condition. Note that if a frame is being sent on the bus during the reset + bus during the reset, the message will be lost. + endmenu # TWAI Configuration menu "UART configuration" diff --git a/components/driver/include/driver/twai.h b/components/driver/include/driver/twai.h index ef240a383d..548ba9cbb7 100644 --- a/components/driver/include/driver/twai.h +++ b/components/driver/include/driver/twai.h @@ -56,22 +56,25 @@ extern "C" { * @note The TWAI_ALERT_AND_LOG flag is not an actual alert, but will configure * the TWAI driver to log to UART when an enabled alert occurs. */ -#define TWAI_ALERT_TX_IDLE 0x0001 /**< Alert(1): No more messages to transmit */ -#define TWAI_ALERT_TX_SUCCESS 0x0002 /**< Alert(2): The previous transmission was successful */ -#define TWAI_ALERT_BELOW_ERR_WARN 0x0004 /**< Alert(4): Both error counters have dropped below error warning limit */ -#define TWAI_ALERT_ERR_ACTIVE 0x0008 /**< Alert(8): TWAI controller has become error active */ -#define TWAI_ALERT_RECOVERY_IN_PROGRESS 0x0010 /**< Alert(16): TWAI controller is undergoing bus recovery */ -#define TWAI_ALERT_BUS_RECOVERED 0x0020 /**< Alert(32): TWAI controller has successfully completed bus recovery */ -#define TWAI_ALERT_ARB_LOST 0x0040 /**< Alert(64): The previous transmission lost arbitration */ -#define TWAI_ALERT_ABOVE_ERR_WARN 0x0080 /**< Alert(128): One of the error counters have exceeded the error warning limit */ -#define TWAI_ALERT_BUS_ERROR 0x0100 /**< Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus */ -#define TWAI_ALERT_TX_FAILED 0x0200 /**< Alert(512): The previous transmission has failed (for single shot transmission) */ -#define TWAI_ALERT_RX_QUEUE_FULL 0x0400 /**< Alert(1024): The RX queue is full causing a frame to be lost */ -#define TWAI_ALERT_ERR_PASS 0x0800 /**< Alert(2048): TWAI controller has become error passive */ -#define TWAI_ALERT_BUS_OFF 0x1000 /**< Alert(4096): Bus-off condition occurred. TWAI controller can no longer influence bus */ -#define TWAI_ALERT_ALL 0x1FFF /**< Bit mask to enable all alerts during configuration */ -#define TWAI_ALERT_NONE 0x0000 /**< Bit mask to disable all alerts during configuration */ -#define TWAI_ALERT_AND_LOG 0x2000 /**< Bit mask to enable alerts to also be logged when they occur. Note that logging from the ISR is disabled if CONFIG_TWAI_ISR_IN_IRAM is enabled (see docs). */ +#define TWAI_ALERT_TX_IDLE 0x00000001 /**< Alert(1): No more messages to transmit */ +#define TWAI_ALERT_TX_SUCCESS 0x00000002 /**< Alert(2): The previous transmission was successful */ +#define TWAI_ALERT_BELOW_ERR_WARN 0x00000004 /**< Alert(4): Both error counters have dropped below error warning limit */ +#define TWAI_ALERT_ERR_ACTIVE 0x00000008 /**< Alert(8): TWAI controller has become error active */ +#define TWAI_ALERT_RECOVERY_IN_PROGRESS 0x00000010 /**< Alert(16): TWAI controller is undergoing bus recovery */ +#define TWAI_ALERT_BUS_RECOVERED 0x00000020 /**< Alert(32): TWAI controller has successfully completed bus recovery */ +#define TWAI_ALERT_ARB_LOST 0x00000040 /**< Alert(64): The previous transmission lost arbitration */ +#define TWAI_ALERT_ABOVE_ERR_WARN 0x00000080 /**< Alert(128): One of the error counters have exceeded the error warning limit */ +#define TWAI_ALERT_BUS_ERROR 0x00000100 /**< Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus */ +#define TWAI_ALERT_TX_FAILED 0x00000200 /**< Alert(512): The previous transmission has failed (for single shot transmission) */ +#define TWAI_ALERT_RX_QUEUE_FULL 0x00000400 /**< Alert(1024): The RX queue is full causing a frame to be lost */ +#define TWAI_ALERT_ERR_PASS 0x00000800 /**< Alert(2048): TWAI controller has become error passive */ +#define TWAI_ALERT_BUS_OFF 0x00001000 /**< Alert(4096): Bus-off condition occurred. TWAI controller can no longer influence bus */ +#define TWAI_ALERT_RX_FIFO_OVERRUN 0x00002000 /**< Alert(8192): An RX FIFO overrun has occurred */ +#define TWAI_ALERT_TX_RETRIED 0x00004000 /**< Alert(16384): An message transmission was cancelled and retried due to an errata workaround */ +#define TWAI_ALERT_PERIPH_RESET 0x00008000 /**< Alert(32768): The TWAI controller was reset */ +#define TWAI_ALERT_ALL 0x0000FFFF /**< Bit mask to enable all alerts during configuration */ +#define TWAI_ALERT_NONE 0x00000000 /**< Bit mask to disable all alerts during configuration */ +#define TWAI_ALERT_AND_LOG 0x00010000 /**< Bit mask to enable alerts to also be logged when they occur. Note that logging from the ISR is disabled if CONFIG_TWAI_ISR_IN_IRAM is enabled (see docs). */ /** @endcond */ @@ -117,7 +120,8 @@ typedef struct { uint32_t tx_error_counter; /**< Current value of Transmit Error Counter */ uint32_t rx_error_counter; /**< Current value of Receive Error Counter */ uint32_t tx_failed_count; /**< Number of messages that failed transmissions */ - uint32_t rx_missed_count; /**< Number of messages that were lost due to a full RX queue */ + uint32_t rx_missed_count; /**< Number of messages that were lost due to a full RX queue (or errata workaround if enabled) */ + uint32_t rx_overrun_count; /**< Number of messages that were lost due to a RX FIFO overrun */ uint32_t arb_lost_count; /**< Number of instances arbitration was lost */ uint32_t bus_error_count; /**< Number of instances a bus error has occurred */ } twai_status_info_t; diff --git a/components/driver/linker.lf b/components/driver/linker.lf new file mode 100644 index 0000000000..c922171372 --- /dev/null +++ b/components/driver/linker.lf @@ -0,0 +1,9 @@ + +[mapping:driver] +archive: libdriver.a +entries: + # TWAI workarounds that require periph_module_reset() won't work if cache is disabled due to the use of switch jump + # tables in periph_module_reset(). We prevent any part of periph_module_reset() (either text or RO data) from being + # placed in flash. + if TWAI_ISR_IN_IRAM = y && (TWAI_ERRATA_FIX_RX_FRAME_INVALID = y || TWAI_ERRATA_FIX_RX_FIFO_CORRUPT = y): + periph_ctrl: periph_module_reset (noflash) \ No newline at end of file diff --git a/components/driver/periph_ctrl.c b/components/driver/periph_ctrl.c index abb5a8a1b5..7b69e86c56 100644 --- a/components/driver/periph_ctrl.c +++ b/components/driver/periph_ctrl.c @@ -13,6 +13,7 @@ // limitations under the License. #include "freertos/FreeRTOS.h" #include "hal/clk_gate_ll.h" +#include "esp_attr.h" #include "driver/periph_ctrl.h" static portMUX_TYPE periph_spinlock = portMUX_INITIALIZER_UNLOCKED; diff --git a/components/driver/twai.c b/components/driver/twai.c index d69c1c428a..b688db93bb 100644 --- a/components/driver/twai.c +++ b/components/driver/twai.c @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "soc/soc_caps.h" #include "sdkconfig.h" #include "freertos/FreeRTOS.h" @@ -28,6 +27,7 @@ #include "driver/gpio.h" #include "driver/periph_ctrl.h" #include "driver/twai.h" +#include "soc/soc_caps.h" #include "soc/twai_periph.h" #include "hal/twai_hal.h" @@ -68,6 +68,7 @@ typedef struct { twai_state_t state; twai_mode_t mode; uint32_t rx_missed_count; + uint32_t rx_overrun_count; uint32_t tx_failed_count; uint32_t arb_lost_count; uint32_t bus_error_count; @@ -127,19 +128,49 @@ TWAI_ISR_ATTR static void twai_alert_handler(uint32_t alert_code, int *alert_req static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req) { +#ifdef TWAI_SUPPORTS_RX_STATUS uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context); - for (int i = 0; i < msg_count; i++) { + for (uint32_t i = 0; i < msg_count; i++) { twai_hal_frame_t frame; - twai_hal_read_rx_buffer_and_clear(&twai_context, &frame); - //Copy frame into RX Queue - if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) { - p_twai_obj->rx_msg_count++; - } else { - p_twai_obj->rx_missed_count++; - twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req); + if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) { + //Valid frame copied from RX buffer + if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) { + p_twai_obj->rx_msg_count++; + } else { //Failed to send to queue + p_twai_obj->rx_missed_count++; + twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req); + } + } else { //Failed to read from RX buffer because message is overrun + p_twai_obj->rx_overrun_count++; + twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req); } } +#else //TWAI_SUPPORTS_RX_STATUS + uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context); + bool overrun = false; + //Clear all valid RX frames + for (int i = 0; i < msg_count; i++) { + twai_hal_frame_t frame; + if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) { + //Valid frame copied from RX buffer + if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) { + p_twai_obj->rx_msg_count++; + } else { + p_twai_obj->rx_missed_count++; + twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req); + } + } else { + overrun = true; + break; + } + } + //All remaining frames are treated as overrun. Clear them all + if (overrun) { + p_twai_obj->rx_overrun_count += twai_hal_clear_rx_fifo_overrun(&twai_context); + twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req); + } +#endif //TWAI_SUPPORTS_RX_STATUS } static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req) @@ -175,56 +206,65 @@ TWAI_ISR_ATTR static void twai_intr_handler_main(void *arg) { BaseType_t task_woken = pdFALSE; int alert_req = 0; - uint32_t event; + uint32_t events; TWAI_ENTER_CRITICAL_ISR(); - if (p_twai_obj == NULL) { //Incase intr occurs whilst driver is being uninstalled + if (p_twai_obj == NULL) { //In case intr occurs whilst driver is being uninstalled TWAI_EXIT_CRITICAL_ISR(); return; } - event = twai_hal_decode_interrupt_events(&twai_context); - if (event & TWAI_HAL_EVENT_RX_BUFF_FRAME) { + events = twai_hal_get_events(&twai_context); //Get the events that triggered the interrupt + +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) + if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) { + twai_hal_prepare_for_reset(&twai_context); + periph_module_reset(PERIPH_TWAI_MODULE); + twai_hal_recover_from_reset(&twai_context); + p_twai_obj->rx_missed_count += twai_hal_get_reset_lost_rx_cnt(&twai_context); + twai_alert_handler(TWAI_ALERT_PERIPH_RESET, &alert_req); + } +#endif + if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME) { + //Note: This event will never occur if there is a periph reset event twai_handle_rx_buffer_frames(&task_woken, &alert_req); } - //TX command should be the last command related handler to be called, so that - //other command register bits do not overwrite the TX command bit. - if (event & TWAI_HAL_EVENT_TX_BUFF_FREE) { + if (events & TWAI_HAL_EVENT_TX_BUFF_FREE) { twai_handle_tx_buffer_frame(&task_woken, &alert_req); } //Handle events that only require alerting (i.e. no handler) - if (event & TWAI_HAL_EVENT_BUS_OFF) { + if (events & TWAI_HAL_EVENT_BUS_OFF) { p_twai_obj->state = TWAI_STATE_BUS_OFF; twai_alert_handler(TWAI_ALERT_BUS_OFF, &alert_req); } - if (event & TWAI_HAL_EVENT_BUS_RECOV_CPLT) { + if (events & TWAI_HAL_EVENT_BUS_RECOV_CPLT) { p_twai_obj->state = TWAI_STATE_STOPPED; twai_alert_handler(TWAI_ALERT_BUS_RECOVERED, &alert_req); } - if (event & TWAI_HAL_EVENT_BUS_ERR) { + if (events & TWAI_HAL_EVENT_BUS_ERR) { p_twai_obj->bus_error_count++; twai_alert_handler(TWAI_ALERT_BUS_ERROR, &alert_req); } - if (event & TWAI_HAL_EVENT_ARB_LOST) { + if (events & TWAI_HAL_EVENT_ARB_LOST) { p_twai_obj->arb_lost_count++; twai_alert_handler(TWAI_ALERT_ARB_LOST, &alert_req); } - if (event & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) { + if (events & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) { //Bus-recovery in progress. TEC has dropped below error warning limit twai_alert_handler(TWAI_ALERT_RECOVERY_IN_PROGRESS, &alert_req); } - if (event & TWAI_HAL_EVENT_ERROR_PASSIVE) { + if (events & TWAI_HAL_EVENT_ERROR_PASSIVE) { //Entered error passive twai_alert_handler(TWAI_ALERT_ERR_PASS, &alert_req); } - if (event & TWAI_HAL_EVENT_ERROR_ACTIVE) { + if (events & TWAI_HAL_EVENT_ERROR_ACTIVE) { //Returned to error active twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, &alert_req); } - if (event & TWAI_HAL_EVENT_ABOVE_EWL) { + if (events & TWAI_HAL_EVENT_ABOVE_EWL) { //TEC or REC surpassed error warning limit twai_alert_handler(TWAI_ALERT_ABOVE_ERR_WARN, &alert_req); } - if (event & TWAI_HAL_EVENT_BELOW_EWL) { + if (events & TWAI_HAL_EVENT_BELOW_EWL) { //TEC and REC are both below error warning twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, &alert_req); } @@ -644,6 +684,7 @@ esp_err_t twai_get_status_info(twai_status_info_t *status_info) status_info->msgs_to_rx = p_twai_obj->rx_msg_count; status_info->tx_failed_count = p_twai_obj->tx_failed_count; status_info->rx_missed_count = p_twai_obj->rx_missed_count; + status_info->rx_overrun_count = p_twai_obj->rx_overrun_count; status_info->arb_lost_count = p_twai_obj->arb_lost_count; status_info->bus_error_count = p_twai_obj->bus_error_count; status_info->state = p_twai_obj->state; diff --git a/components/soc/include/hal/twai_hal.h b/components/soc/include/hal/twai_hal.h index 9a2f021b31..960dd77e0c 100644 --- a/components/soc/include/hal/twai_hal.h +++ b/components/soc/include/hal/twai_hal.h @@ -26,13 +26,14 @@ extern "C" { #include #include +#include "sdkconfig.h" #include "hal/twai_types.h" #include "hal/twai_ll.h" /* ------------------------- Defines and Typedefs --------------------------- */ -#define TWAI_HAL_SET_FLAG(var, flag) ((var) |= (flag)) -#define TWAI_HAL_RESET_FLAG(var, flag) ((var) &= ~(flag)) +#define TWAI_HAL_SET_BITS(var, flag) ((var) |= (flag)) +#define TWAI_HAL_CLEAR_BITS(var, flag) ((var) &= ~(flag)) //HAL state flags #define TWAI_HAL_STATE_FLAG_RUNNING (1 << 0) //Controller is active (not in reset mode) @@ -41,8 +42,11 @@ extern "C" { #define TWAI_HAL_STATE_FLAG_ERR_PASSIVE (1 << 3) //TEC or REC is >= 128 #define TWAI_HAL_STATE_FLAG_BUS_OFF (1 << 4) //Bus-off due to TEC >= 256 #define TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED (1 << 5) //Transmit buffer is occupied +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) +#define TWAI_HAL_STATE_FLAG_TX_NEED_RETRY (1 << 7) //TX needs to be restarted due to errata workarounds +#endif -//Error active interrupt related +//Interrupt Events #define TWAI_HAL_EVENT_BUS_OFF (1 << 0) #define TWAI_HAL_EVENT_BUS_RECOV_CPLT (1 << 1) #define TWAI_HAL_EVENT_BUS_RECOV_PROGRESS (1 << 2) @@ -54,14 +58,22 @@ extern "C" { #define TWAI_HAL_EVENT_ARB_LOST (1 << 8) #define TWAI_HAL_EVENT_RX_BUFF_FRAME (1 << 9) #define TWAI_HAL_EVENT_TX_BUFF_FREE (1 << 10) +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) +#define TWAI_HAL_EVENT_NEED_PERIPH_RESET (1 << 11) +#endif + +typedef twai_ll_frame_buffer_t twai_hal_frame_t; typedef struct { twai_dev_t *dev; uint32_t state_flags; +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) + twai_hal_frame_t tx_frame_save; + twai_ll_reg_save_t reg_save; + uint8_t rx_msg_cnt_save; +#endif } twai_hal_context_t; -typedef twai_ll_frame_buffer_t twai_hal_frame_t; - /* ---------------------------- Init and Config ----------------------------- */ /** @@ -125,7 +137,7 @@ void twai_hal_stop(twai_hal_context_t *hal_ctx); */ static inline void twai_hal_start_bus_recovery(twai_hal_context_t *hal_ctx) { - TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RECOVERING); + TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RECOVERING); twai_ll_exit_reset_mode(hal_ctx->dev); } @@ -193,22 +205,23 @@ static inline bool twai_hal_check_state_flags(twai_hal_context_t *hal_ctx, uint3 /* ----------------------------- Event Handling ----------------------------- */ /** - * @brief Decode current events that triggered an interrupt + * @brief Get a bit mask of the events that triggered that triggered an interrupt * - * This function should be the called at the beginning of an ISR. This - * function will do the following: - * - Read and clear interrupts - * - Decode current events that triggered an interrupt + * This function should be called at the beginning of an interrupt. This function will do the following: + * - Read and clear interrupt register + * - Calculate what events have triggered the interrupt * - Respond to low latency interrupt events - * - Bus off: Change to LOM to free TEC/REC + * - Bus off: Change to LOM to freeze TEC/REC. Errata 1 Fix * - Recovery complete: Enter reset mode - * - Clear ECC and ALC - * - Update state flags based on events that have occurred. + * - Clear ECC and ALC so that their interrupts are re-armed + * - Update HAL state flags based on interrupts that have occurred. + * - For the ESP32, check for errata conditions. If a HW reset is required, this function + * will set the TWAI_HAL_EVENT_NEED_PERIPH_RESET event. * * @param hal_ctx Context of the HAL layer * @return Bit mask of events that have occurred */ -uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx); +uint32_t twai_hal_get_events(twai_hal_context_t *hal_ctx); /* ------------------------------- TX and RX -------------------------------- */ @@ -260,24 +273,105 @@ void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_f * @brief Copy a frame from the RX buffer and release * * This function copies a frame from the RX buffer, then release the buffer (so - * that it loads the next frame in the RX FIFO). + * that it loads the next frame in the RX FIFO). False is returned under the + * following conditions: + * - On the ESP32S2, false is returned if the RX buffer points to an overrun frame + * - On the ESP32, false is returned if the RX buffer points to the first overrun + * frame in the RX FIFO * * @param hal_ctx Context of the HAL layer * @param rx_frame Pointer to structure to store RX frame + * @return True if a valid frame was copied and released. False if overrun. */ -static inline void twai_hal_read_rx_buffer_and_clear(twai_hal_context_t *hal_ctx, twai_hal_frame_t *rx_frame) +static inline bool twai_hal_read_rx_buffer_and_clear(twai_hal_context_t *hal_ctx, twai_hal_frame_t *rx_frame) { +#ifdef TWAI_SUPPORTS_RX_STATUS + if (twai_ll_get_status(hal_ctx->dev) & TWAI_LL_STATUS_MS) { + //Release the buffer for this particular overrun frame + twai_ll_set_cmd_release_rx_buffer(hal_ctx->dev); + return false; + } +#else + if (twai_ll_get_status(hal_ctx->dev) & TWAI_LL_STATUS_DOS) { + //No need to release RX buffer as we'll be releaseing all RX frames in continuously later + return false; + } +#endif twai_ll_get_rx_buffer(hal_ctx->dev, rx_frame); twai_ll_set_cmd_release_rx_buffer(hal_ctx->dev); - /* - * Todo: Support overrun handling by: - * - Check overrun status bit. Return false if overrun - */ + return true; } +#ifndef TWAI_SUPPORTS_RX_STATUS +/** + * @brief Clear the RX FIFO of overrun frames + * + * This function will clear the RX FIFO of overrun frames. The RX message count + * will return to 0 after calling this function. + * + * @param hal_ctx Context of the HAL layer + * @return Number of overrun messages cleared from RX FIFO + */ +static inline uint32_t twai_hal_clear_rx_fifo_overrun(twai_hal_context_t *hal_ctx) +{ + uint32_t msg_cnt = 0; + //Note: Need to keep polling th rx message counter incase another message arrives whilst clearing + while (twai_ll_get_rx_msg_count(hal_ctx->dev) > 0) { + twai_ll_set_cmd_release_rx_buffer(hal_ctx->dev); + msg_cnt++; + } + //Set a clear data overrun command to clear the data overrun status bit + twai_ll_set_cmd_clear_data_overrun(hal_ctx->dev); -//Todo: Decode ALC register -//Todo: Decode error code capture + return msg_cnt; +} +#endif //TWAI_SUPPORTS_RX_STATUS + +/* --------------------------- Errata Workarounds --------------------------- */ + +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) +/** + * @brief Prepare the peripheral for a HW reset + * + * Some HW erratas will require the peripheral be reset. This function should be + * called if twai_hal_get_events() returns the TWAI_HAL_EVENT_NEED_PERIPH_RESET event. + * Preparing for a reset involves the following: + * - Checking if a reset will cancel a TX. If so, mark that we need to retry that message after the reset + * - Save how many RX messages were lost due to this reset + * - Enter reset mode to stop any the peripheral from receiving any bus activity + * - Store the regsiter state of the peripheral + * + * @param hal_ctx Context of the HAL layer + */ +void twai_hal_prepare_for_reset(twai_hal_context_t *hal_ctx); + +/** + * @brief Recover the peripheral after a HW reset + * + * This should be called after calling twai_hal_prepare_for_reset() and then + * executing the HW reset. + * Recovering the peripheral from a HW reset involves the following: + * - Restoring the previously saved register state + * - Exiting reset mode to allow receiving of bus activity + * - Retrying any TX message that was cancelled by the HW reset + * + * @param hal_ctx Context of the HAL layer + */ +void twai_hal_recover_from_reset(twai_hal_context_t *hal_ctx); + +/** + * @brief Get how many RX messages were lost due to HW reset + * + * @note The number of lost RX messages are saved during twai_hal_prepare_for_reset() + * + * @param hal_ctx Context of the HAL layer + * @return uint32_t Number of RX messages lost due to HW reset + */ +static inline uint32_t twai_hal_get_reset_lost_rx_cnt(twai_hal_context_t *hal_ctx) +{ + return hal_ctx->rx_msg_cnt_save; +} +#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) #ifdef __cplusplus } diff --git a/components/soc/soc/esp32s2/include/soc/twai_caps.h b/components/soc/soc/esp32s2/include/soc/twai_caps.h index 4ad5fbd829..3cc1136bd1 100644 --- a/components/soc/soc/esp32s2/include/soc/twai_caps.h +++ b/components/soc/soc/esp32s2/include/soc/twai_caps.h @@ -20,6 +20,7 @@ extern "C" { #define TWAI_BRP_MIN 2 #define TWAI_BRP_MAX 32768 +#define TWAI_SUPPORTS_RX_STATUS 1 #ifdef __cplusplus } diff --git a/components/soc/src/esp32/include/hal/can_hal.h b/components/soc/src/esp32/include/hal/can_hal.h index 1deba898fb..919157aa89 100644 --- a/components/soc/src/esp32/include/hal/can_hal.h +++ b/components/soc/src/esp32/include/hal/can_hal.h @@ -109,7 +109,7 @@ static inline bool can_hal_check_state_flags(can_hal_context_t *hal_ctx, uint32_ /* ----------------------------- Event Handling ----------------------------- */ static inline uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx) { - return twai_hal_decode_interrupt_events(hal_ctx); + return twai_hal_decode_interrupt(hal_ctx); } /* ------------------------------- TX and RX -------------------------------- */ diff --git a/components/soc/src/esp32/include/hal/twai_ll.h b/components/soc/src/esp32/include/hal/twai_ll.h index be9fd28381..44dd55c989 100644 --- a/components/soc/src/esp32/include/hal/twai_ll.h +++ b/components/soc/src/esp32/include/hal/twai_ll.h @@ -86,6 +86,70 @@ typedef union { _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes"); +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) +/** + * Some errata workarounds will require a hardware reset of the peripheral. Thus + * certain registers must be saved before the reset, and then restored after the + * reset. This structure is used to hold some of those registers. + */ +typedef struct { + uint8_t mode_reg; + uint8_t interrupt_enable_reg; + uint8_t bus_timing_0_reg; + uint8_t bus_timing_1_reg; + uint8_t error_warning_limit_reg; + uint8_t acr_reg[4]; + uint8_t amr_reg[4]; + uint8_t rx_error_counter_reg; + uint8_t tx_error_counter_reg; + uint8_t clock_divider_reg; +} __attribute__((packed)) twai_ll_reg_save_t; +#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) + +#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID +typedef enum { + TWAI_LL_ERR_BIT = 0, + TWAI_LL_ERR_FORM, + TWAI_LL_ERR_STUFF, + TWAI_LL_ERR_OTHER, + TWAI_LL_ERR_MAX, +} twai_ll_err_type_t; + +typedef enum { + TWAI_LL_ERR_DIR_TX = 0, + TWAI_LL_ERR_DIR_RX, + TWAI_LL_ERR_DIR_MAX, +} twai_ll_err_dir_t; + +typedef enum { + TWAI_LL_ERR_SEG_SOF = 0, + TWAI_LL_ERR_SEG_ID_28_21 = 2, + TWAI_LL_ERR_SEG_SRTR = 4, + TWAI_LL_ERR_SEG_IDE = 5, + TWAI_LL_ERR_SEG_ID_20_18 = 6, + TWAI_LL_ERR_SEG_ID_17_13 = 7, + TWAI_LL_ERR_SEG_CRC_SEQ = 8, + TWAI_LL_ERR_SEG_R0 = 9, + TWAI_LL_ERR_SEG_DATA = 10, + TWAI_LL_ERR_SEG_DLC = 11, + TWAI_LL_ERR_SEG_RTR = 12, + TWAI_LL_ERR_SEG_R1 = 13, + TWAI_LL_ERR_SEG_ID_4_0 = 14, + TWAI_LL_ERR_SEG_ID_12_5 = 15, + TWAI_LL_ERR_SEG_ACT_FLAG = 17, + TWAI_LL_ERR_SEG_INTER = 18, + TWAI_LL_ERR_SEG_SUPERPOS = 19, + TWAI_LL_ERR_SEG_PASS_FLAG = 22, + TWAI_LL_ERR_SEG_ERR_DELIM = 23, + TWAI_LL_ERR_SEG_CRC_DELIM = 24, + TWAI_LL_ERR_SEG_ACK_SLOT = 25, + TWAI_LL_ERR_SEG_EOF = 26, + TWAI_LL_ERR_SEG_ACK_DELIM = 27, + TWAI_LL_ERR_SEG_OVRLD_FLAG = 28, + TWAI_LL_ERR_SEG_MAX = 29, +} twai_ll_err_seg_t; +#endif + /* ---------------------------- Mode Register ------------------------------- */ /** @@ -402,6 +466,19 @@ static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) (void)hw->error_code_capture_reg.val; } +#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID +static inline void twai_ll_parse_err_code_cap(twai_dev_t *hw, + twai_ll_err_type_t *type, + twai_ll_err_dir_t *dir, + twai_ll_err_seg_t *seg) +{ + uint32_t ecc = hw->error_code_capture_reg.val; + *type = (twai_ll_err_type_t) ((ecc >> 6) & 0x3); + *dir = (twai_ll_err_dir_t) ((ecc >> 5) & 0x1); + *seg = (twai_ll_err_seg_t) (ecc & 0x1F); +} +#endif + /* ----------------------------- EWL Register ------------------------------- */ /** @@ -694,6 +771,65 @@ static inline void twai_ll_enable_extended_reg_layout(twai_dev_t *hw) hw->clock_divider_reg.cm = 1; } +/* ------------------------- Register Save/Restore -------------------------- */ + +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) +/** + * @brief Saves the current values of the TWAI controller's registers + * + * This function saves the current values of the some of the TWAI controller's + * registers in preparation for a hardware reset of the controller. + * + * @param hw Start address of the TWAI registers + * @param reg_save Pointer to structure to store register values + * @note Must be called in reset mode so that config registers become accessible. + * @note Some registers are cleared on entering reset mode so must be saved + * separate from this function. + */ +static inline void twai_ll_save_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save) +{ + reg_save->mode_reg = (uint8_t) hw->mode_reg.val; + reg_save->interrupt_enable_reg = (uint8_t) hw->interrupt_enable_reg.val; + reg_save->bus_timing_0_reg = (uint8_t) hw->bus_timing_0_reg.val; + reg_save->bus_timing_1_reg = (uint8_t) hw->bus_timing_1_reg.val; + reg_save->error_warning_limit_reg = (uint8_t) hw->error_warning_limit_reg.val; + for (int i = 0; i < 4; i++) { + reg_save->acr_reg[i] = hw->acceptance_filter.acr[i].byte; + reg_save->amr_reg[i] = hw->acceptance_filter.amr[i].byte; + } + reg_save->rx_error_counter_reg = (uint8_t) hw->rx_error_counter_reg.val; + reg_save->tx_error_counter_reg = (uint8_t) hw->tx_error_counter_reg.val; + reg_save->clock_divider_reg = (uint8_t) hw->clock_divider_reg.val; +} + +/** + * @brief Restores the previous values of the TWAI controller's registers + * + * This function restores the previous values of some of the TWAI controller's + * registers following a hardware reset of the controller. + * + * @param hw Start address of the TWAI registers + * @param reg_save Pointer to structure to storing register values to restore + * @note Must be called in reset mode so that config registers become accessible + * @note Some registers are read only thus cannot be restored + */ +static inline void twai_ll_restore_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save) +{ + hw->mode_reg.val = reg_save->mode_reg; + hw->interrupt_enable_reg.val = reg_save->interrupt_enable_reg; + hw->bus_timing_0_reg.val = reg_save->bus_timing_0_reg; + hw->bus_timing_1_reg.val = reg_save->bus_timing_1_reg; + hw->error_warning_limit_reg.val = reg_save->error_warning_limit_reg; + for (int i = 0; i < 4; i++) { + hw->acceptance_filter.acr[i].byte = reg_save->acr_reg[i]; + hw->acceptance_filter.amr[i].byte = reg_save->amr_reg[i]; + } + hw->rx_error_counter_reg.val = reg_save->rx_error_counter_reg; + hw->tx_error_counter_reg.val = reg_save->tx_error_counter_reg; + hw->clock_divider_reg.val = reg_save->clock_divider_reg; +} +#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) + #ifdef __cplusplus } #endif diff --git a/components/soc/src/esp32s2/include/hal/twai_ll.h b/components/soc/src/esp32s2/include/hal/twai_ll.h index 8b27b58f94..764bf1dd61 100644 --- a/components/soc/src/esp32s2/include/hal/twai_ll.h +++ b/components/soc/src/esp32s2/include/hal/twai_ll.h @@ -42,6 +42,7 @@ extern "C" { #define TWAI_LL_STATUS_TS (0x1 << 5) //Transmit Status #define TWAI_LL_STATUS_ES (0x1 << 6) //Error Status #define TWAI_LL_STATUS_BS (0x1 << 7) //Bus Status +#define TWAI_LL_STATUS_MS (0x1 << 8) //Miss Status #define TWAI_LL_INTR_RI (0x1 << 0) //Receive Interrupt #define TWAI_LL_INTR_TI (0x1 << 1) //Transmit Interrupt diff --git a/components/soc/src/hal/twai_hal.c b/components/soc/src/hal/twai_hal.c index 34d7cd1e1c..3daf0f44dd 100644 --- a/components/soc/src/hal/twai_hal.c +++ b/components/soc/src/hal/twai_hal.c @@ -13,7 +13,9 @@ // limitations under the License. #include +#include "sdkconfig.h" #include "hal/twai_hal.h" +#include "soc/twai_periph.h" //Default values written to various registers on initialization #define TWAI_HAL_INIT_TEC 0 @@ -69,8 +71,8 @@ void twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode) { twai_ll_set_mode(hal_ctx->dev, mode); //Set operating mode (void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts - TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING); - twai_ll_exit_reset_mode(hal_ctx->dev); + TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING); + twai_ll_exit_reset_mode(hal_ctx->dev); } void twai_hal_stop(twai_hal_context_t *hal_ctx) @@ -79,6 +81,5 @@ void twai_hal_stop(twai_hal_context_t *hal_ctx) (void) twai_ll_get_and_clear_intrs(hal_ctx->dev); twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode //Any TX is immediately halted on entering reset mode - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED); - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING); -} \ No newline at end of file + TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED | TWAI_HAL_STATE_FLAG_RUNNING); +} diff --git a/components/soc/src/hal/twai_hal_iram.c b/components/soc/src/hal/twai_hal_iram.c index 4262914a4c..ee9691d07a 100644 --- a/components/soc/src/hal/twai_hal_iram.c +++ b/components/soc/src/hal/twai_hal_iram.c @@ -13,83 +13,173 @@ // limitations under the License. #include +#include +#include "sdkconfig.h" #include "hal/twai_hal.h" +#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT +//Errata condition occurs at 64 messages. Threshold set to 62 to prevent the chance of failing to detect errata condition. +#define TWAI_RX_FIFO_CORRUPT_THRESH 62 +#endif + /* ----------------------------- Event Handling ----------------------------- */ -uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx) +/** + * Helper functions that can decode what events have been triggered based on + * the values of the interrupt, status, TEC and REC registers. The HAL context's + * state flags are also updated based on the events that have triggered. + */ +static inline uint32_t twai_hal_decode_interrupt(twai_hal_context_t *hal_ctx) { uint32_t events = 0; - //Read interrupt, status uint32_t interrupts = twai_ll_get_and_clear_intrs(hal_ctx->dev); uint32_t status = twai_ll_get_status(hal_ctx->dev); uint32_t tec = twai_ll_get_tec(hal_ctx->dev); uint32_t rec = twai_ll_get_rec(hal_ctx->dev); + uint32_t state_flags = hal_ctx->state_flags; //Error Warning Interrupt set whenever Error or Bus Status bit changes if (interrupts & TWAI_LL_INTR_EI) { - if (status & TWAI_LL_STATUS_BS) { - //Currently in BUS OFF state + if (status & TWAI_LL_STATUS_BS) { //Currently in BUS OFF state if (status & TWAI_LL_STATUS_ES) { //EWL is exceeded, thus must have entered BUS OFF - twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY); //Set to listen only to freeze tec and rec - events |= TWAI_HAL_EVENT_BUS_OFF; - TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_BUS_OFF); - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING); + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_OFF); + TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_BUS_OFF); //Any TX would have been halted by entering bus off. Reset its flag - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED); + TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_RUNNING | TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED); } else { //Below EWL. Therefore TEC is counting down in bus recovery - events |= TWAI_HAL_EVENT_BUS_RECOV_PROGRESS; + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_RECOV_PROGRESS); } - } else { - //Not in BUS OFF + } else { //Not in BUS OFF if (status & TWAI_LL_STATUS_ES) { //Just Exceeded EWL - events |= TWAI_HAL_EVENT_ABOVE_EWL; - TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN); + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ABOVE_EWL); + TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN); } else if (hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_RECOVERING) { //Previously undergoing bus recovery. Thus means bus recovery complete - twai_ll_enter_reset_mode(hal_ctx->dev); //Enter reset mode to stop the peripheral - events |= TWAI_HAL_EVENT_BUS_RECOV_CPLT; - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RECOVERING); - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_BUS_OFF); + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_RECOV_CPLT); + TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_RECOVERING | TWAI_HAL_STATE_FLAG_BUS_OFF); } else { //Just went below EWL - events |= TWAI_HAL_EVENT_BELOW_EWL; - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN); + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BELOW_EWL); + TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_WARN); } } } - //Receive Interrupt set whenever RX FIFO is not empty + //Receive Interrupt set whenever RX FIFO is not empty if (interrupts & TWAI_LL_INTR_RI) { - events |= TWAI_HAL_EVENT_RX_BUFF_FRAME; + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_RX_BUFF_FRAME); } //Transmit interrupt set whenever TX buffer becomes free +#ifdef CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST + if ((interrupts & TWAI_LL_INTR_TI || hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) && status & TWAI_LL_STATUS_TBS) { +#else if (interrupts & TWAI_LL_INTR_TI) { - events |= TWAI_HAL_EVENT_TX_BUFF_FREE; - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED); +#endif + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_TX_BUFF_FREE); + TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED); } //Error Passive Interrupt on transition from error active to passive or vice versa if (interrupts & TWAI_LL_INTR_EPI) { if (tec >= TWAI_ERR_PASS_THRESH || rec >= TWAI_ERR_PASS_THRESH) { - events |= TWAI_HAL_EVENT_ERROR_PASSIVE; - TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE); + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ERROR_PASSIVE); + TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE); } else { - events |= TWAI_HAL_EVENT_ERROR_ACTIVE; - TWAI_HAL_RESET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE); + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ERROR_ACTIVE); + TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE); } } //Bus error interrupt triggered on a bus error (e.g. bit, ACK, stuff etc) if (interrupts & TWAI_LL_INTR_BEI) { - twai_ll_clear_err_code_cap(hal_ctx->dev); - events |= TWAI_HAL_EVENT_BUS_ERR; + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_ERR); } //Arbitration Lost Interrupt triggered on losing arbitration if (interrupts & TWAI_LL_INTR_ALI) { - twai_ll_clear_arb_lost_cap(hal_ctx->dev); - events |= TWAI_HAL_EVENT_ARB_LOST; + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ARB_LOST); } + hal_ctx->state_flags = state_flags; return events; } +uint32_t twai_hal_get_events(twai_hal_context_t *hal_ctx) +{ + uint32_t events = twai_hal_decode_interrupt(hal_ctx); + + //Handle low latency events + if (events & TWAI_HAL_EVENT_BUS_OFF) { + twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY); //Freeze TEC/REC by entering LOM +#ifdef CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC + //Errata workaround: Force REC to 0 by re-triggering bus-off (by setting TEC to 0 then 255) + twai_ll_set_tec(hal_ctx->dev, 0); + twai_ll_set_tec(hal_ctx->dev, 255); + (void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear the re-triggered bus-off interrupt +#endif + } + if (events & TWAI_HAL_EVENT_BUS_RECOV_CPLT) { + twai_ll_enter_reset_mode(hal_ctx->dev); //Enter reset mode to stop the controller + } + if (events & TWAI_HAL_EVENT_BUS_ERR) { +#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID + twai_ll_err_type_t type; + twai_ll_err_dir_t dir; + twai_ll_err_seg_t seg; + twai_ll_parse_err_code_cap(hal_ctx->dev, &type, &dir, &seg); //Decode error interrupt + //Check for errata condition (RX message has bus error at particular segments) + if (dir == TWAI_LL_ERR_DIR_RX && + ((seg == TWAI_LL_ERR_SEG_DATA || seg == TWAI_LL_ERR_SEG_CRC_SEQ) || + (seg == TWAI_LL_ERR_SEG_ACK_DELIM && type == TWAI_LL_ERR_OTHER))) { + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_NEED_PERIPH_RESET); + } +#endif + twai_ll_clear_err_code_cap(hal_ctx->dev); + } + if (events & TWAI_HAL_EVENT_ARB_LOST) { + twai_ll_clear_arb_lost_cap(hal_ctx->dev); + } +#ifdef CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT + //Check for errata condition (rx_msg_count >= corruption_threshold) + if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME && twai_ll_get_rx_msg_count(hal_ctx->dev) >= TWAI_RX_FIFO_CORRUPT_THRESH) { + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_NEED_PERIPH_RESET); + } +#endif +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) + if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) { + //A peripheral reset will invalidate an RX event; + TWAI_HAL_CLEAR_BITS(events, (TWAI_HAL_EVENT_RX_BUFF_FRAME)); + } +#endif + return events; +} + +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) +void twai_hal_prepare_for_reset(twai_hal_context_t *hal_ctx) +{ + uint32_t status = twai_ll_get_status(hal_ctx->dev); + if (!(status & TWAI_LL_STATUS_TBS)) { //Transmit buffer is NOT free, indicating an Ongoing TX will be cancelled by the HW reset + TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_NEED_RETRY); + //Note: Even if the TX completes right after this, we still consider it will be retried. + //Worst case the same message will get sent twice. + } + //Some register must saved before entering reset mode + hal_ctx->rx_msg_cnt_save = (uint8_t) twai_ll_get_rx_msg_count(hal_ctx->dev); + twai_ll_enter_reset_mode(hal_ctx->dev); //Enter reset mode to stop the controller + twai_ll_save_reg(hal_ctx->dev, &hal_ctx->reg_save); //Save remaining registers after entering reset mode +} + +void twai_hal_recover_from_reset(twai_hal_context_t *hal_ctx) +{ + twai_ll_enter_reset_mode(hal_ctx->dev); + twai_ll_enable_extended_reg_layout(hal_ctx->dev); + twai_ll_restore_reg(hal_ctx->dev, &hal_ctx->reg_save); + twai_ll_exit_reset_mode(hal_ctx->dev); + (void) twai_ll_get_and_clear_intrs(hal_ctx->dev); + + if (hal_ctx->state_flags & TWAI_HAL_STATE_FLAG_TX_NEED_RETRY) { + //HW reset has cancelled a TX. Re-transmit here + twai_hal_set_tx_buffer_and_transmit(hal_ctx, &hal_ctx->tx_frame_save); + TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_NEED_RETRY); + } +} +#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) + void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_frame_t *tx_frame) { //Copy frame into tx buffer @@ -106,5 +196,9 @@ void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_f } else { twai_ll_set_cmd_tx(hal_ctx->dev); } - TWAI_HAL_SET_FLAG(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED); -} \ No newline at end of file + TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED); +#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) + //Save transmitted frame in case we need to retry + memcpy(&hal_ctx->tx_frame_save, tx_frame, sizeof(twai_hal_frame_t)); +#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) +}