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 ac7550297f..548ba9cbb7 100644 --- a/components/driver/include/driver/twai.h +++ b/components/driver/include/driver/twai.h @@ -40,9 +40,9 @@ extern "C" { * configured. The other members of the general configuration structure are * assigned default values. */ -#define TWAI_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode) {.mode = op_mode, .tx_io = tx_io_num, .rx_io = rx_io_num, \ - .clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, \ - .tx_queue_len = 5, .rx_queue_len = 5, \ +#define TWAI_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode) {.mode = op_mode, .tx_io = tx_io_num, .rx_io = rx_io_num, \ + .clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, \ + .tx_queue_len = 5, .rx_queue_len = 5, \ .alerts_enabled = TWAI_ALERT_NONE, .clkout_divider = 0, \ .intr_flags = ESP_INTR_FLAG_LEVEL1} @@ -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; @@ -168,9 +172,9 @@ esp_err_t twai_driver_uninstall(void); * * This function starts the TWAI driver, putting the TWAI driver into the running * state. This allows the TWAI driver to participate in TWAI bus activities such - * as transmitting/receiving messages. The RX queue is reset in this function, - * clearing any unread messages. This function can only be called when the TWAI - * driver is in the stopped state. + * as transmitting/receiving messages. The TX and RX queue are reset in this function, + * clearing any messages that are unread or pending transmission. This function + * can only be called when the TWAI driver is in the stopped state. * * @return * - ESP_OK: TWAI driver is now running 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 4cbdf7406c..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" @@ -57,14 +57,6 @@ #define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun (bit[3]) and brp_div (bit[4]) -//Control flags -#define CTRL_FLAG_STOPPED 0x001 //TWAI peripheral in stopped state -#define CTRL_FLAG_RECOVERING 0x002 //Bus is undergoing bus recovery -#define CTRL_FLAG_ERR_WARN 0x004 //TEC or REC is >= error warning limit -#define CTRL_FLAG_ERR_PASSIVE 0x008 //TEC or REC is >= 128 -#define CTRL_FLAG_BUS_OFF 0x010 //Bus-off due to TEC >= 256 -#define CTRL_FLAG_TX_BUFF_OCCUPIED 0x020 //Transmit buffer is occupied - #define ALERT_LOG_LEVEL_WARNING TWAI_ALERT_ARB_LOST //Alerts above and including this level use ESP_LOGW #define ALERT_LOG_LEVEL_ERROR TWAI_ALERT_TX_FAILED //Alerts above and including this level use ESP_LOGE @@ -73,9 +65,10 @@ //Control structure for TWAI driver typedef struct { //Control and status members - uint32_t control_flags; + 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; @@ -129,102 +122,55 @@ TWAI_ISR_ATTR static void twai_alert_handler(uint32_t alert_code, int *alert_req ESP_EARLY_LOGI(TWAI_TAG, "Alert %d", alert_code); } } -#endif +#endif //CONFIG_TWAI_ISR_IN_IRAM } } -static inline void twai_handle_bus_off(int *alert_req) -{ - //Bus-Off condition. TEC should set and held at 127, REC should be 0, reset mode entered - TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_BUS_OFF); - /* Note: REC is still allowed to increase during bus-off. REC > err_warn - can prevent "bus recovery complete" interrupt from occurring. Set to - listen only mode to freeze REC. */ - twai_hal_handle_bus_off(&twai_context); - twai_alert_handler(TWAI_ALERT_BUS_OFF, alert_req); -} - -static inline void twai_handle_recovery_complete(int *alert_req) -{ - //Bus recovery complete. - bool recov_cplt = twai_hal_handle_bus_recov_cplt(&twai_context); - assert(recov_cplt); - - //Reset and set flags to the equivalent of the stopped state - TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_RECOVERING | CTRL_FLAG_ERR_WARN | - CTRL_FLAG_ERR_PASSIVE | CTRL_FLAG_BUS_OFF | - CTRL_FLAG_TX_BUFF_OCCUPIED); - TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED); - twai_alert_handler(TWAI_ALERT_BUS_RECOVERED, alert_req); -} - -static inline void twai_handle_recovery_in_progress(int * alert_req) -{ - //Bus-recovery in progress. TEC has dropped below error warning limit - twai_alert_handler(TWAI_ALERT_RECOVERY_IN_PROGRESS, alert_req); -} - -static inline void twai_handle_above_ewl(int *alert_req) -{ - //TEC or REC surpassed error warning limit - TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_WARN); - twai_alert_handler(TWAI_ALERT_ABOVE_ERR_WARN, alert_req); -} - -static inline void twai_handle_below_ewl(int *alert_req) -{ - //TEC and REC are both below error warning - TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_WARN); - twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, alert_req); -} - -static inline void twai_handle_error_passive(int *alert_req) -{ - //Entered error passive - TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_PASSIVE); - twai_alert_handler(TWAI_ALERT_ERR_PASS, alert_req); -} - -static inline void twai_handle_error_active(int *alert_req) -{ - //Returned to error active - TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_PASSIVE); - twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, alert_req); -} - -static inline void twai_handle_bus_error(int *alert_req) -{ - // ECC register is read to re-arm bus error interrupt. ECC is not used - twai_hal_handle_bus_error(&twai_context); - p_twai_obj->bus_error_count++; - twai_alert_handler(TWAI_ALERT_BUS_ERROR, alert_req); -} - -static inline void twai_handle_arb_lost(int *alert_req) -{ - //ALC register is read to re-arm arb lost interrupt. ALC is not used - twai_hal_handle_arb_lost(&twai_context); - p_twai_obj->arb_lost_count++; - twai_alert_handler(TWAI_ALERT_ARB_LOST, 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); } } - //Todo: Add Software Filters - //Todo: Check for data overrun of RX FIFO, then trigger alert +#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) @@ -252,7 +198,6 @@ static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *aler } } else { //No more frames to transmit - TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED); twai_alert_handler(TWAI_ALERT_TX_IDLE, alert_req); } } @@ -261,49 +206,69 @@ 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, p_twai_obj->control_flags & CTRL_FLAG_RECOVERING); + events = twai_hal_get_events(&twai_context); //Get the events that triggered the interrupt - if (event & TWAI_HAL_EVENT_BUS_OFF) { - twai_handle_bus_off(&alert_req); +#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); } - if (event & TWAI_HAL_EVENT_BUS_RECOV_CPLT) { - twai_handle_recovery_complete(&alert_req); - } - if (event & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) { - twai_handle_recovery_in_progress(&alert_req); - } - if (event & TWAI_HAL_EVENT_ABOVE_EWL) { - twai_handle_above_ewl(&alert_req); - } - if (event & TWAI_HAL_EVENT_BELOW_EWL) { - twai_handle_below_ewl(&alert_req); - } - if (event & TWAI_HAL_EVENT_ERROR_PASSIVE) { - twai_handle_error_passive(&alert_req); - } - if (event & TWAI_HAL_EVENT_ERROR_ACTIVE) { - twai_handle_error_active(&alert_req); - } - if (event & TWAI_HAL_EVENT_BUS_ERR) { - twai_handle_bus_error(&alert_req); - } - if (event & TWAI_HAL_EVENT_ARB_LOST) { - twai_handle_arb_lost(&alert_req); - } - if (event & TWAI_HAL_EVENT_RX_BUFF_FRAME) { +#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 related handlers should be called last, so that other commands - //do not overwrite the TX command related bits in the command register. - 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 (events & TWAI_HAL_EVENT_BUS_OFF) { + p_twai_obj->state = TWAI_STATE_BUS_OFF; + twai_alert_handler(TWAI_ALERT_BUS_OFF, &alert_req); + } + 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 (events & TWAI_HAL_EVENT_BUS_ERR) { + p_twai_obj->bus_error_count++; + twai_alert_handler(TWAI_ALERT_BUS_ERROR, &alert_req); + } + if (events & TWAI_HAL_EVENT_ARB_LOST) { + p_twai_obj->arb_lost_count++; + twai_alert_handler(TWAI_ALERT_ARB_LOST, &alert_req); + } + 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 (events & TWAI_HAL_EVENT_ERROR_PASSIVE) { + //Entered error passive + twai_alert_handler(TWAI_ALERT_ERR_PASS, &alert_req); + } + if (events & TWAI_HAL_EVENT_ERROR_ACTIVE) { + //Returned to error active + twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, &alert_req); + } + 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 (events & TWAI_HAL_EVENT_BELOW_EWL) { + //TEC and REC are both below error warning + twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, &alert_req); + } + TWAI_EXIT_CRITICAL_ISR(); if (p_twai_obj->alert_semphr != NULL && alert_req) { @@ -335,6 +300,7 @@ static void twai_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_set_pull_mode(clkout, GPIO_FLOATING); gpio_matrix_out(clkout, TWAI_CLKOUT_IDX, false, false); gpio_pad_select_gpio(clkout); + } //Configure bus status pin (Optional) @@ -433,6 +399,8 @@ cleanup: return NULL; } + + /* ---------------------------- Public Functions ---------------------------- */ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config) @@ -464,7 +432,7 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_ TWAI_CHECK(p_twai_obj_dummy != NULL, ESP_ERR_NO_MEM); //Initialize flags and variables. All other members are already set to zero by twai_alloc_driver_obj() - p_twai_obj_dummy->control_flags = CTRL_FLAG_STOPPED; + p_twai_obj_dummy->state = TWAI_STATE_STOPPED; p_twai_obj_dummy->mode = g_config->mode; p_twai_obj_dummy->alerts_enabled = g_config->alerts_enabled; @@ -483,7 +451,6 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_ bool init = twai_hal_init(&twai_context); assert(init); twai_hal_configure(&twai_context, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider); - //Todo: Allow interrupt to be registered to specified CPU TWAI_EXIT_CRITICAL(); //Allocate GPIO and Interrupts @@ -496,7 +463,6 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_ return ESP_OK; //TWAI module is still in reset mode, users need to call twai_start() afterwards err: - //Free can driver object twai_free_driver_obj(p_twai_obj_dummy); return ret; } @@ -508,8 +474,7 @@ esp_err_t twai_driver_uninstall(void) TWAI_ENTER_CRITICAL(); //Check state TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE); - TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE); - //Todo: Add check to see if in reset mode. //Enter reset mode to stop any TWAI bus activity + TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_STOPPED || p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE); //Clear registers by reading twai_hal_deinit(&twai_context); periph_module_disable(PERIPH_TWAI_MODULE); //Disable TWAI peripheral @@ -533,18 +498,18 @@ esp_err_t twai_start(void) //Check state TWAI_ENTER_CRITICAL(); TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE); - TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & CTRL_FLAG_STOPPED, ESP_ERR_INVALID_STATE); + TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_STOPPED, ESP_ERR_INVALID_STATE); - //Reset RX queue, and RX message count + //Reset RX queue, RX message count, amd TX queue xQueueReset(p_twai_obj->rx_queue); + if (p_twai_obj->tx_queue != NULL) { + xQueueReset(p_twai_obj->tx_queue); + } p_twai_obj->rx_msg_count = 0; - //Todo: Add assert to see if in reset mode. //Should already be in bus-off mode, set again to make sure + p_twai_obj->tx_msg_count = 0; + twai_hal_start(&twai_context, p_twai_obj->mode); - //Currently in listen only mode, need to set to mode specified by configuration - bool started = twai_hal_start(&twai_context, p_twai_obj->mode); - assert(started); - - TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED); + p_twai_obj->state = TWAI_STATE_RUNNING; TWAI_EXIT_CRITICAL(); return ESP_OK; } @@ -554,19 +519,16 @@ esp_err_t twai_stop(void) //Check state TWAI_ENTER_CRITICAL(); TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE); - TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE); + TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_RUNNING, ESP_ERR_INVALID_STATE); - bool stopped = twai_hal_stop(&twai_context); - assert(stopped); - - TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED); - TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED); + twai_hal_stop(&twai_context); //Reset TX Queue and message count if (p_twai_obj->tx_queue != NULL) { xQueueReset(p_twai_obj->tx_queue); } p_twai_obj->tx_msg_count = 0; + p_twai_obj->state = TWAI_STATE_STOPPED; TWAI_EXIT_CRITICAL(); @@ -583,18 +545,17 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait) TWAI_ENTER_CRITICAL(); //Check State TWAI_CHECK_FROM_CRIT(!(p_twai_obj->mode == TWAI_MODE_LISTEN_ONLY), ESP_ERR_NOT_SUPPORTED); - TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE); + TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_RUNNING, ESP_ERR_INVALID_STATE); //Format frame esp_err_t ret = ESP_FAIL; twai_hal_frame_t tx_frame; twai_hal_format_frame(message, &tx_frame); //Check if frame can be sent immediately - if ((p_twai_obj->tx_msg_count == 0) && !(p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) { + if (p_twai_obj->tx_msg_count == 0) { //No other frames waiting to transmit. Bypass queue and transmit immediately twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame); p_twai_obj->tx_msg_count++; - TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED); ret = ESP_OK; } TWAI_EXIT_CRITICAL(); @@ -606,19 +567,19 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait) } else if (xQueueSend(p_twai_obj->tx_queue, &tx_frame, ticks_to_wait) == pdTRUE) { //Copied to TX Queue TWAI_ENTER_CRITICAL(); - if (p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)) { - //TX queue was reset (due to stop/bus_off), remove copied frame from queue to prevent transmission - int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0); - assert(res == pdTRUE); - ret = ESP_ERR_INVALID_STATE; - } else if ((p_twai_obj->tx_msg_count == 0) && !(p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) { - //TX buffer was freed during copy, manually trigger transmission - int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0); - assert(res == pdTRUE); - twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame); - p_twai_obj->tx_msg_count++; - TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED); - ret = ESP_OK; + if ((!twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED)) && uxQueueMessagesWaiting(p_twai_obj->tx_queue) > 0) { + //If the TX buffer is free but the TX queue is not empty. Check if we need to manually start a transmission + if (twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_BUS_OFF) || !twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_RUNNING)) { + //TX buffer became free due to bus-off or is no longer running. No need to start a transmission + ret = ESP_ERR_INVALID_STATE; + } else { + //Manually start a transmission + int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0); + assert(res == pdTRUE); + twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame); + p_twai_obj->tx_msg_count++; + ret = ESP_OK; + } } else { //Frame was copied to queue, waiting to be transmitted p_twai_obj->tx_msg_count++; @@ -694,20 +655,17 @@ esp_err_t twai_initiate_recovery(void) TWAI_ENTER_CRITICAL(); //Check state TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE); - TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & CTRL_FLAG_BUS_OFF, ESP_ERR_INVALID_STATE); - TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & CTRL_FLAG_RECOVERING), ESP_ERR_INVALID_STATE); + TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE); //Reset TX Queue/Counters if (p_twai_obj->tx_queue != NULL) { xQueueReset(p_twai_obj->tx_queue); } p_twai_obj->tx_msg_count = 0; - TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED); - TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_RECOVERING); //Trigger start of recovery process - bool started = twai_hal_start_bus_recovery(&twai_context); - assert(started); + twai_hal_start_bus_recovery(&twai_context); + p_twai_obj->state = TWAI_STATE_RECOVERING; TWAI_EXIT_CRITICAL(); return ESP_OK; @@ -726,17 +684,10 @@ 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; - if (p_twai_obj->control_flags & CTRL_FLAG_RECOVERING) { - status_info->state = TWAI_STATE_RECOVERING; - } else if (p_twai_obj->control_flags & CTRL_FLAG_BUS_OFF) { - status_info->state = TWAI_STATE_BUS_OFF; - } else if (p_twai_obj->control_flags & CTRL_FLAG_STOPPED) { - status_info->state = TWAI_STATE_STOPPED; - } else { - status_info->state = TWAI_STATE_RUNNING; - } + status_info->state = p_twai_obj->state; TWAI_EXIT_CRITICAL(); return ESP_OK; @@ -750,7 +701,7 @@ esp_err_t twai_clear_transmit_queue(void) TWAI_ENTER_CRITICAL(); //If a message is currently undergoing transmission, the tx interrupt handler will decrement tx_msg_count - p_twai_obj->tx_msg_count = (p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0; + p_twai_obj->tx_msg_count = twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0; xQueueReset(p_twai_obj->tx_queue); TWAI_EXIT_CRITICAL(); diff --git a/components/soc/include/hal/twai_hal.h b/components/soc/include/hal/twai_hal.h index 592c01680d..960dd77e0c 100644 --- a/components/soc/include/hal/twai_hal.h +++ b/components/soc/include/hal/twai_hal.h @@ -26,30 +26,54 @@ extern "C" { #include #include +#include "sdkconfig.h" #include "hal/twai_types.h" #include "hal/twai_ll.h" /* ------------------------- Defines and Typedefs --------------------------- */ -//Error active interrupt related -#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) -#define TWAI_HAL_EVENT_ABOVE_EWL (1 << 3) -#define TWAI_HAL_EVENT_BELOW_EWL (1 << 4) -#define TWAI_HAL_EVENT_ERROR_PASSIVE (1 << 5) -#define TWAI_HAL_EVENT_ERROR_ACTIVE (1 << 6) -#define TWAI_HAL_EVENT_BUS_ERR (1 << 7) -#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) +#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) +#define TWAI_HAL_STATE_FLAG_RECOVERING (1 << 1) //Bus is undergoing bus recovery +#define TWAI_HAL_STATE_FLAG_ERR_WARN (1 << 2) //TEC or REC is >= error warning limit +#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 + +//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) +#define TWAI_HAL_EVENT_ABOVE_EWL (1 << 3) +#define TWAI_HAL_EVENT_BELOW_EWL (1 << 4) +#define TWAI_HAL_EVENT_ERROR_PASSIVE (1 << 5) +#define TWAI_HAL_EVENT_ERROR_ACTIVE (1 << 6) +#define TWAI_HAL_EVENT_BUS_ERR (1 << 7) +#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 ----------------------------- */ /** @@ -93,9 +117,8 @@ void twai_hal_configure(twai_hal_context_t *hal_ctx, const twai_timing_config_t * * @param hal_ctx Context of the HAL layer * @param mode Operating mode - * @return True if successfully started, false otherwise. */ -bool twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode); +void twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode); /** * @brief Stop the TWAI peripheral @@ -104,19 +127,18 @@ bool twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode); * setting the operating mode to Listen Only so that REC is frozen. * * @param hal_ctx Context of the HAL layer - * @return True if successfully stopped, false otherwise. */ -bool twai_hal_stop(twai_hal_context_t *hal_ctx); +void twai_hal_stop(twai_hal_context_t *hal_ctx); /** * @brief Start bus recovery * * @param hal_ctx Context of the HAL layer - * @return True if successfully started bus recovery, false otherwise. */ -static inline bool twai_hal_start_bus_recovery(twai_hal_context_t *hal_ctx) +static inline void twai_hal_start_bus_recovery(twai_hal_context_t *hal_ctx) { - return twai_ll_exit_reset_mode(hal_ctx->dev); + TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RECOVERING); + twai_ll_exit_reset_mode(hal_ctx->dev); } /** @@ -163,73 +185,43 @@ static inline bool twai_hal_check_last_tx_successful(twai_hal_context_t *hal_ctx return twai_ll_is_last_tx_successful((hal_ctx)->dev); } +/** + * @brief Check if certain HAL state flags are set + * + * The HAL will maintain a record of the controller's state via a set of flags. + * These flags are automatically maintained (i.e., set and reset) inside various + * HAL function calls. This function checks if certain flags are currently set. + * + * @param hal_ctx Context of the HAL layer + * @param check_flags Bit mask of flags to check + * @return True if one or more of the flags in check_flags are set + */ + +static inline bool twai_hal_check_state_flags(twai_hal_context_t *hal_ctx, uint32_t check_flags) +{ + return hal_ctx->state_flags & check_flags; +} + /* ----------------------------- 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 called on every TWAI interrupt. It will read (and - * thereby clear) the interrupt register, then determine what events have - * occurred to trigger the 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 freeze TEC/REC. Errata 1 Fix + * - Recovery complete: Enter reset mode + * - 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 - * @param bus_recovering Whether the TWAI peripheral was previous undergoing bus recovery * @return Bit mask of events that have occurred */ -uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx, bool bus_recovering); - -/** - * @brief Handle bus recovery complete - * - * This function should be called on an bus recovery complete event. It simply - * enters reset mode to stop bus activity. - * - * @param hal_ctx Context of the HAL layer - * @return True if successfully handled bus recovery completion, false otherwise. - */ -static inline bool twai_hal_handle_bus_recov_cplt(twai_hal_context_t *hal_ctx) -{ - return twai_ll_enter_reset_mode((hal_ctx)->dev); -} - -/** - * @brief Handle arbitration lost - * - * This function should be called on an arbitration lost event. It simply clears - * the clears the ALC register. - * - * @param hal_ctx Context of the HAL layer - */ -static inline void twai_hal_handle_arb_lost(twai_hal_context_t *hal_ctx) -{ - twai_ll_clear_arb_lost_cap((hal_ctx)->dev); -} - -/** - * @brief Handle bus error - * - * This function should be called on an bus error event. It simply clears - * the clears the ECC register. - * - * @param hal_ctx Context of the HAL layer - */ -static inline void twai_hal_handle_bus_error(twai_hal_context_t *hal_ctx) -{ - twai_ll_clear_err_code_cap((hal_ctx)->dev); -} - -/** - * @brief Handle BUS OFF - * - * This function should be called on a BUS OFF event. It simply changes the - * mode to LOM to freeze REC - * - * @param hal_ctx Context of the HAL layer - */ -static inline void twai_hal_handle_bus_off(twai_hal_context_t *hal_ctx) -{ - twai_ll_set_mode((hal_ctx)->dev, TWAI_MODE_LISTEN_ONLY); -} +uint32_t twai_hal_get_events(twai_hal_context_t *hal_ctx); /* ------------------------------- TX and RX -------------------------------- */ @@ -281,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 9a1c12464d..919157aa89 100644 --- a/components/soc/src/esp32/include/hal/can_hal.h +++ b/components/soc/src/esp32/include/hal/can_hal.h @@ -66,19 +66,19 @@ static inline void can_hal_configure(can_hal_context_t *hal_ctx, const can_timin /* -------------------------------- Actions --------------------------------- */ -static inline bool can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode) +static inline void can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode) { - return twai_hal_start(hal_ctx, mode); + twai_hal_start(hal_ctx, mode); } -static inline bool can_hal_stop(can_hal_context_t *hal_ctx) +static inline void can_hal_stop(can_hal_context_t *hal_ctx) { - return twai_hal_stop(hal_ctx); + twai_hal_stop(hal_ctx); } -static inline bool can_hal_start_bus_recovery(can_hal_context_t *hal_ctx) +static inline void can_hal_start_bus_recovery(can_hal_context_t *hal_ctx) { - return twai_hal_start_bus_recovery(hal_ctx); + twai_hal_start_bus_recovery(hal_ctx); } static inline uint32_t can_hal_get_tec(can_hal_context_t *hal_ctx) @@ -101,37 +101,22 @@ static inline bool can_hal_check_last_tx_successful(can_hal_context_t *hal_ctx) return twai_hal_check_last_tx_successful(hal_ctx); } +static inline bool can_hal_check_state_flags(can_hal_context_t *hal_ctx, uint32_t check_flags) +{ + return twai_hal_check_state_flags(hal_ctx, check_flags); +} + /* ----------------------------- Event Handling ----------------------------- */ -static inline uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering) { - return twai_hal_decode_interrupt_events(hal_ctx, bus_recovering); -} - -static inline bool can_hal_handle_bus_recov_cplt(can_hal_context_t *hal_ctx) -{ - return twai_hal_handle_bus_recov_cplt(hal_ctx); -} - -static inline void can_hal_handle_arb_lost(can_hal_context_t *hal_ctx) -{ - twai_hal_handle_arb_lost(hal_ctx); -} - -static inline void can_hal_handle_bus_error(can_hal_context_t *hal_ctx) -{ - twai_hal_handle_bus_error(hal_ctx); -} - -static inline void can_hal_handle_bus_off(can_hal_context_t *hal_ctx) -{ - twai_hal_handle_bus_off(hal_ctx); +static inline uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx) { + return twai_hal_decode_interrupt(hal_ctx); } /* ------------------------------- TX and RX -------------------------------- */ static inline void can_hal_format_frame(const can_message_t *message, can_hal_frame_t *frame) { - twai_hal_format_frame(message, frame); + twai_hal_format_frame(message, frame); } static inline void can_hal_parse_frame(can_hal_frame_t *frame, can_message_t *message) diff --git a/components/soc/src/esp32/include/hal/can_ll.h b/components/soc/src/esp32/include/hal/can_ll.h index 9fa4817924..5cbd10273d 100644 --- a/components/soc/src/esp32/include/hal/can_ll.h +++ b/components/soc/src/esp32/include/hal/can_ll.h @@ -54,14 +54,14 @@ typedef twai_ll_frame_buffer_t can_ll_frame_buffer_t; /* ---------------------------- Mode Register ------------------------------- */ -static inline bool can_ll_enter_reset_mode(can_dev_t *hw) +static inline void can_ll_enter_reset_mode(can_dev_t *hw) { - return twai_ll_enter_reset_mode(hw); + twai_ll_enter_reset_mode(hw); } -static inline bool can_ll_exit_reset_mode(can_dev_t *hw) +static inline void can_ll_exit_reset_mode(can_dev_t *hw) { - return twai_ll_exit_reset_mode(hw); + twai_ll_exit_reset_mode(hw); } static inline bool can_ll_is_in_reset_mode(can_dev_t *hw) diff --git a/components/soc/src/esp32/include/hal/twai_ll.h b/components/soc/src/esp32/include/hal/twai_ll.h index fab0da4fa6..44dd55c989 100644 --- a/components/soc/src/esp32/include/hal/twai_ll.h +++ b/components/soc/src/esp32/include/hal/twai_ll.h @@ -31,27 +31,26 @@ extern "C" { #include "sdkconfig.h" #include "hal/twai_types.h" #include "soc/twai_periph.h" -#include "soc/twai_struct.h" #include "hal/hal_defs.h" /* ------------------------- Defines and Typedefs --------------------------- */ -#define TWAI_LL_STATUS_RBS (0x1 << 0) -#define TWAI_LL_STATUS_DOS (0x1 << 1) -#define TWAI_LL_STATUS_TBS (0x1 << 2) -#define TWAI_LL_STATUS_TCS (0x1 << 3) -#define TWAI_LL_STATUS_RS (0x1 << 4) -#define TWAI_LL_STATUS_TS (0x1 << 5) -#define TWAI_LL_STATUS_ES (0x1 << 6) -#define TWAI_LL_STATUS_BS (0x1 << 7) +#define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status +#define TWAI_LL_STATUS_DOS (0x1 << 1) //Data Overrun Status +#define TWAI_LL_STATUS_TBS (0x1 << 2) //Transmit Buffer Status +#define TWAI_LL_STATUS_TCS (0x1 << 3) //Transmission Complete Status +#define TWAI_LL_STATUS_RS (0x1 << 4) //Receive Status +#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_INTR_RI (0x1 << 0) -#define TWAI_LL_INTR_TI (0x1 << 1) -#define TWAI_LL_INTR_EI (0x1 << 2) +#define TWAI_LL_INTR_RI (0x1 << 0) //Receive Interrupt +#define TWAI_LL_INTR_TI (0x1 << 1) //Transmit Interrupt +#define TWAI_LL_INTR_EI (0x1 << 2) //Error Interrupt //Data overrun interrupt not supported in SW due to HW peculiarities -#define TWAI_LL_INTR_EPI (0x1 << 5) -#define TWAI_LL_INTR_ALI (0x1 << 6) -#define TWAI_LL_INTR_BEI (0x1 << 7) +#define TWAI_LL_INTR_EPI (0x1 << 5) //Error Passive Interrupt +#define TWAI_LL_INTR_ALI (0x1 << 6) //Arbitration Lost Interrupt +#define TWAI_LL_INTR_BEI (0x1 << 7) //Bus Error Interrupt /* * The following frame structure has an NEARLY identical bit field layout to @@ -87,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 ------------------------------- */ /** @@ -97,14 +160,12 @@ _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should b * in order to write the majority of configuration registers. * * @param hw Start address of the TWAI registers - * @return true if reset mode was entered successfully * * @note Reset mode is automatically entered on BUS OFF condition */ -static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw) +static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 1; - return hw->mode_reg.rm; } /** @@ -115,14 +176,12 @@ static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw) * operating mode. * * @param hw Start address of the TWAI registers - * @return true if reset mode was exit successfully * * @note Reset mode must be exit to initiate BUS OFF recovery */ -static inline bool twai_ll_exit_reset_mode(twai_dev_t *hw) +static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 0; - return !(hw->mode_reg.rm); } /** @@ -137,10 +196,10 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) /** * @brief Set operating mode of TWAI controller - * + * * @param hw Start address of the TWAI registers * @param mode Operating mode - * + * * @note Must be called in reset mode */ static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) @@ -309,8 +368,6 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) return hw->status_reg.tcs; } -//Todo: Add stand alone status bit check functions when necessary - /* -------------------------- Interrupt Register ---------------------------- */ /** @@ -393,7 +450,6 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) { (void)hw->arbitration_lost_captue_reg.val; - //Todo: Decode ALC register } /* ----------------------------- ECC Register ------------------------------- */ @@ -408,9 +464,21 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) { (void)hw->error_code_capture_reg.val; - //Todo: Decode error code capture } +#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 ------------------------------- */ /** @@ -556,7 +624,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * This function encodes a message into a frame structure. The frame structure * has an identical layout to the TX buffer, allowing the frame structure to be * directly copied into TX buffer. - * + * * @param[in] 11bit or 29bit ID * @param[in] dlc Data length code * @param[in] data Pointer to an 8 byte array containing data. NULL if no data @@ -665,9 +733,9 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) /** * @brief Set CLKOUT Divider and enable/disable - * + * * Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be - * 1, or any even number from 2 to 14. Set the divider to 0 to disable CLKOUT. + * 1, or any even number from 2 to 14. Set the divider to 0 to disable CLKOUT. * * @param hw Start address of the TWAI registers * @param divider Divider for CLKOUT. Set to 0 to disable CLKOUT @@ -703,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 567f92ed63..764bf1dd61 100644 --- a/components/soc/src/esp32s2/include/hal/twai_ll.h +++ b/components/soc/src/esp32s2/include/hal/twai_ll.h @@ -30,28 +30,27 @@ extern "C" { #include #include "hal/twai_types.h" #include "soc/twai_periph.h" -#include "soc/twai_struct.h" #include "hal/hal_defs.h" /* ------------------------- Defines and Typedefs --------------------------- */ -#define TWAI_LL_STATUS_RBS (0x1 << 0) -#define TWAI_LL_STATUS_DOS (0x1 << 1) -#define TWAI_LL_STATUS_TBS (0x1 << 2) -#define TWAI_LL_STATUS_TCS (0x1 << 3) -#define TWAI_LL_STATUS_RS (0x1 << 4) -#define TWAI_LL_STATUS_TS (0x1 << 5) -#define TWAI_LL_STATUS_ES (0x1 << 6) -#define TWAI_LL_STATUS_BS (0x1 << 7) -//Todo: Add Miss status support +#define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status +#define TWAI_LL_STATUS_DOS (0x1 << 1) //Data Overrun Status +#define TWAI_LL_STATUS_TBS (0x1 << 2) //Transmit Buffer Status +#define TWAI_LL_STATUS_TCS (0x1 << 3) //Transmission Complete Status +#define TWAI_LL_STATUS_RS (0x1 << 4) //Receive Status +#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) -#define TWAI_LL_INTR_TI (0x1 << 1) -#define TWAI_LL_INTR_EI (0x1 << 2) +#define TWAI_LL_INTR_RI (0x1 << 0) //Receive Interrupt +#define TWAI_LL_INTR_TI (0x1 << 1) //Transmit Interrupt +#define TWAI_LL_INTR_EI (0x1 << 2) //Error Interrupt //Data overrun interrupt not supported in SW due to HW peculiarities -#define TWAI_LL_INTR_EPI (0x1 << 5) -#define TWAI_LL_INTR_ALI (0x1 << 6) -#define TWAI_LL_INTR_BEI (0x1 << 7) +#define TWAI_LL_INTR_EPI (0x1 << 5) //Error Passive Interrupt +#define TWAI_LL_INTR_ALI (0x1 << 6) //Arbitration Lost Interrupt +#define TWAI_LL_INTR_BEI (0x1 << 7) //Bus Error Interrupt /* * The following frame structure has an NEARLY identical bit field layout to @@ -97,14 +96,12 @@ _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should b * in order to write the majority of configuration registers. * * @param hw Start address of the TWAI registers - * @return true if reset mode was entered successfully * * @note Reset mode is automatically entered on BUS OFF condition */ -static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw) +static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 1; - return hw->mode_reg.rm; } /** @@ -115,14 +112,12 @@ static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw) * operating mode. * * @param hw Start address of the TWAI registers - * @return true if reset mode was exit successfully * * @note Reset mode must be exit to initiate BUS OFF recovery */ -static inline bool twai_ll_exit_reset_mode(twai_dev_t *hw) +static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 0; - return !(hw->mode_reg.rm); } /** @@ -137,10 +132,10 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) /** * @brief Set operating mode of TWAI controller - * + * * @param hw Start address of the TWAI registers * @param mode Operating mode - * + * * @note Must be called in reset mode */ static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) @@ -309,8 +304,6 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) return hw->status_reg.tcs; } -//Todo: Add stand alone status bit check functions when necessary - /* -------------------------- Interrupt Register ---------------------------- */ /** @@ -378,7 +371,6 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) { (void)hw->arbitration_lost_captue_reg.val; - //Todo: Decode ALC register } /* ----------------------------- ECC Register ------------------------------- */ @@ -393,7 +385,6 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) { (void)hw->error_code_capture_reg.val; - //Todo: Decode error code capture } /* ----------------------------- EWL Register ------------------------------- */ @@ -541,7 +532,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * This function encodes a message into a frame structure. The frame structure * has an identical layout to the TX buffer, allowing the frame structure to be * directly copied into TX buffer. - * + * * @param[in] 11bit or 29bit ID * @param[in] dlc Data length code * @param[in] data Pointer to an 8 byte array containing data. NULL if no data @@ -652,8 +643,8 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) * @brief Set CLKOUT Divider and enable/disable * * Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be - * 1, or any even number from 2 to 490. Set the divider to 0 to disable CLKOUT. - * + * 1, or any even number from 2 to 490. Set the divider to 0 to disable CLKOUT. + * * @param hw Start address of the TWAI registers * @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT */ diff --git a/components/soc/src/hal/twai_hal.c b/components/soc/src/hal/twai_hal.c index 18fa58a2b2..3daf0f44dd 100644 --- a/components/soc/src/hal/twai_hal.c +++ b/components/soc/src/hal/twai_hal.c @@ -12,22 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -//Todo: Place the implementation of all common HAL functions here - #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 #define TWAI_HAL_INIT_REC 0 #define TWAI_HAL_INIT_EWL 96 +/* ---------------------------- Init and Config ----------------------------- */ + bool twai_hal_init(twai_hal_context_t *hal_ctx) { //Initialize HAL context hal_ctx->dev = &TWAI; + hal_ctx->state_flags = 0; //Initialize TWAI controller, and set default values to registers - if (!twai_ll_enter_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers + twai_ll_enter_reset_mode(hal_ctx->dev); + if (!twai_ll_is_in_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers return false; } #ifdef TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT @@ -61,21 +65,21 @@ void twai_hal_configure(twai_hal_context_t *hal_ctx, const twai_timing_config_t (void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts } -bool twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode) +/* -------------------------------- Actions --------------------------------- */ + +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 - //Todo: Check if this can be removed (void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts - return twai_ll_exit_reset_mode(hal_ctx->dev); //Return false if failed to exit reset mode + TWAI_HAL_SET_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_RUNNING); + twai_ll_exit_reset_mode(hal_ctx->dev); } -bool twai_hal_stop(twai_hal_context_t *hal_ctx) +void twai_hal_stop(twai_hal_context_t *hal_ctx) { - if (!twai_ll_enter_reset_mode(hal_ctx->dev)) { - return false; - } - //Todo: Check if this can be removed + twai_ll_enter_reset_mode(hal_ctx->dev); (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 - return true; -} \ No newline at end of file + //Any TX is immediately halted on entering reset mode + 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 1857a947c4..ee9691d07a 100644 --- a/components/soc/src/hal/twai_hal_iram.c +++ b/components/soc/src/hal/twai_hal_iram.c @@ -12,57 +12,174 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include +#include +#include "sdkconfig.h" #include "hal/twai_hal.h" -uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx, bool bus_recovering) +#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 ----------------------------- */ + +/** + * 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; - //Receive Interrupt set whenever RX FIFO is not empty - if (interrupts & TWAI_LL_INTR_RI) { - events |= TWAI_HAL_EVENT_RX_BUFF_FRAME; - } - //Transmit interrupt set whenever TX buffer becomes free - if (interrupts & TWAI_LL_INTR_TI) { - events |= TWAI_HAL_EVENT_TX_BUFF_FREE; - } //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 - //EWL is exceeded, thus must have entered BUS OFF - //Below EWL. Therefore TEC is counting down in bus recovery - //Todo: Check if BUS Recov can be removed for esp32s2 - events |= (status & TWAI_LL_STATUS_ES) ? TWAI_HAL_EVENT_BUS_OFF : TWAI_HAL_EVENT_BUS_RECOV_PROGRESS; - } else { - //Not in BUS OFF - events |= (status & TWAI_LL_STATUS_ES) ? TWAI_HAL_EVENT_ABOVE_EWL : //Just Exceeded EWL - ((bus_recovering) ? //If previously undergoing bus recovery - TWAI_HAL_EVENT_BUS_RECOV_CPLT : - TWAI_HAL_EVENT_BELOW_EWL); + 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_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_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 + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_BUS_RECOV_PROGRESS); + } + } else { //Not in BUS OFF + if (status & TWAI_LL_STATUS_ES) { //Just Exceeded EWL + 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_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 + 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 + if (interrupts & TWAI_LL_INTR_RI) { + 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) { +#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) { - events |= (tec >= TWAI_ERR_PASS_THRESH || rec >= TWAI_ERR_PASS_THRESH) ? TWAI_HAL_EVENT_ERROR_PASSIVE : TWAI_HAL_EVENT_ERROR_ACTIVE; - } - //Arbitration Lost Interrupt triggered on losing arbitration - if (interrupts & TWAI_LL_INTR_ALI) { - events |= TWAI_HAL_EVENT_ARB_LOST; + if (tec >= TWAI_ERR_PASS_THRESH || rec >= TWAI_ERR_PASS_THRESH) { + TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_ERROR_PASSIVE); + TWAI_HAL_SET_BITS(state_flags, TWAI_HAL_STATE_FLAG_ERR_PASSIVE); + } else { + 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) { - 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); + } +#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 @@ -79,4 +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); } -} \ 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) +}