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
This commit is contained in:
Darian Leung 2022-02-10 21:04:09 +08:00
parent 90ee294139
commit a0666b9be8
14 changed files with 532 additions and 107 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,6 +20,7 @@ extern "C" {
#define TWAI_BRP_MIN 2
#define TWAI_BRP_MAX 32768
#define TWAI_SUPPORTS_RX_STATUS 1
#ifdef __cplusplus
}

View File

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

View File

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

View File

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

View File

@ -13,7 +13,9 @@
// limitations under the License.
#include <stddef.h>
#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);
}
TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED | TWAI_HAL_STATE_FLAG_RUNNING);
}

View File

@ -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_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);
}
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)
}