Explorar o código

TWAI: FIFO overrun handling and errata workarounds

This commit adds handling for FIFO overruns and
adds workarounds for HW errats on the ESP32.

Closes https://github.com/espressif/esp-idf/issues/2519
Closes https://github.com/espressif/esp-idf/issues/4276
Darian Leung %!s(int64=4) %!d(string=hai) anos
pai
achega
07291fdd27

+ 1 - 1
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

+ 2 - 1
components/driver/CMakeLists.txt

@@ -76,7 +76,8 @@ idf_component_register(SRCS "${srcs}"
                     INCLUDE_DIRS ${includes}
                     PRIV_INCLUDE_DIRS "include/driver"
                     PRIV_REQUIRES efuse esp_pm esp_timer esp_ipc
-                    REQUIRES esp_ringbuf freertos soc hal esp_hw_support)
+                    REQUIRES esp_ringbuf freertos soc hal esp_hw_support
+                    LDFRAGMENTS linker.lf)
 # (REQUIRES cannot hide soc headers, since many arguments in the driver headers are chip-dependent)
 
 # uses C11 atomic feature

+ 42 - 0
components/driver/Kconfig

@@ -87,6 +87,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"

+ 21 - 17
components/driver/include/driver/twai.h

@@ -53,22 +53,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 */
 
@@ -114,7 +117,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;

+ 9 - 0
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)

+ 1 - 0
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;

+ 64 - 23
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"
 #include "esp_rom_gpio.h"
@@ -69,6 +69,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;
@@ -128,19 +129,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 SOC_TWAI_SUPPORTS_RX_STATUS
     uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
 
     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++;
+        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   //SOC_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 {
-            p_twai_obj->rx_missed_count++;
-            twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
+            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  //SOC_TWAI_SUPPORTS_RX_STATUS
 }
 
 static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req)
@@ -176,56 +207,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;

+ 1 - 1
components/hal/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 -------------------------------- */

+ 136 - 0
components/hal/esp32/include/hal/twai_ll.h

@@ -85,6 +85,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 ------------------------------- */
 
 /**
@@ -401,6 +465,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 ------------------------------- */
 
 /**
@@ -693,6 +770,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

+ 1 - 0
components/hal/esp32c3/include/hal/twai_ll.h

@@ -41,6 +41,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

+ 1 - 0
components/hal/esp32s2/include/hal/twai_ll.h

@@ -41,6 +41,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

+ 1 - 0
components/hal/esp32s3/include/hal/twai_ll.h

@@ -41,6 +41,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

+ 117 - 23
components/hal/include/hal/twai_hal.h

@@ -26,13 +26,14 @@ extern "C" {
 
 #include <stddef.h>
 #include <stdbool.h>
+#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 SOC_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 SOC_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);
+
+    return msg_cnt;
 }
+#endif  //SOC_TWAI_SUPPORTS_RX_STATUS
 
+/* --------------------------- Errata Workarounds --------------------------- */
 
-//Todo: Decode ALC register
-//Todo: Decode error code capture
+#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
 }

+ 3 - 3
components/hal/twai_hal.c

@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include <stddef.h>
+#include "sdkconfig.h"
 #include "hal/twai_hal.h"
 #include "soc/soc_caps.h"
 
@@ -70,7 +71,7 @@ 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_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING);
     twai_ll_exit_reset_mode(hal_ctx->dev);
 }
 
@@ -80,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);
+    TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED | TWAI_HAL_STATE_FLAG_RUNNING);
 }

+ 126 - 32
components/hal/twai_hal_iram.c

@@ -13,83 +13,173 @@
 // limitations under the License.
 
 #include <stddef.h>
+#include <string.h>
+#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_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);
-        events |= TWAI_HAL_EVENT_ARB_LOST;
     }
+#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);
+    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)
 }

+ 1 - 0
components/soc/esp32c3/include/soc/soc_caps.h

@@ -48,6 +48,7 @@
 /*-------------------------- TWAI CAPS ---------------------------------------*/
 #define SOC_TWAI_BRP_MIN                2
 #define SOC_TWAI_BRP_MAX                32768
+#define SOC_TWAI_SUPPORTS_RX_STATUS     1
 
 /*--------------------------- SHA CAPS ---------------------------------------*/
 

+ 1 - 0
components/soc/esp32s2/include/soc/soc_caps.h

@@ -237,6 +237,7 @@
 /*-------------------------- TWAI CAPS ---------------------------------------*/
 #define SOC_TWAI_BRP_MIN                2
 #define SOC_TWAI_BRP_MAX                32768
+#define SOC_TWAI_SUPPORTS_RX_STATUS     1
 
 /*-------------------------- UART CAPS ---------------------------------------*/
 // ESP32-S2 have 2 UART.

+ 2 - 0
components/soc/esp32s3/include/soc/twai_caps.h

@@ -21,6 +21,8 @@ extern "C" {
 #define SOC_TWAI_BRP_MIN    2
 #define SOC_TWAI_BRP_MAX    32768
 
+#define SOC_TWAI_SUPPORTS_RX_STATUS     1
+
 #ifdef __cplusplus
 }
 #endif