Skip to content

Commit 8ec8f61

Browse files
committed
Merge branch 'bugfix/twai_esp32_errata_workarounds' into 'master'
TWAI: Add FIFO overrun handling and ESP32 hardware errata workarounds Closes IDFGH-3307, IDFGH-393, and IDFGH-2114 See merge request espressif/esp-idf!10041
2 parents 80d0f6c + 2f58060 commit 8ec8f61

File tree

18 files changed

+530
-101
lines changed

18 files changed

+530
-101
lines changed

components/app_trace/linker.lf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ entries:
99
SEGGER_SYSVIEW_Config_FreeRTOS (noflash)
1010
SEGGER_SYSVIEW_FreeRTOS (noflash)
1111

12-
[mapping:driver]
12+
[mapping:app_trace_driver]
1313
archive: libdriver.a
1414
entries:
1515
if SYSVIEW_TS_SOURCE_TIMER_00 = y || SYSVIEW_TS_SOURCE_TIMER_01 = y

components/driver/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ idf_component_register(SRCS "${srcs}"
7676
INCLUDE_DIRS ${includes}
7777
PRIV_INCLUDE_DIRS "include/driver"
7878
PRIV_REQUIRES efuse esp_timer esp_ipc
79-
REQUIRES esp_pm esp_ringbuf freertos soc hal esp_hw_support)
79+
REQUIRES esp_pm esp_ringbuf freertos soc hal esp_hw_support
80+
LDFRAGMENTS linker.lf)
8081
# (REQUIRES cannot hide soc headers, since many arguments in the driver headers are chip-dependent)
8182

8283
# uses C11 atomic feature

components/driver/Kconfig

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,48 @@ menu "Driver configurations"
8787
- Alert logging (i.e., setting of the TWAI_ALERT_AND_LOG flag)
8888
will have no effect.
8989

90+
config TWAI_ERRATA_FIX_BUS_OFF_REC
91+
bool "Add SW workaround for REC change during bus-off"
92+
depends on IDF_TARGET_ESP32
93+
default n
94+
help
95+
When the bus-off condition is reached, the REC should be reset to 0 and frozen (via LOM) by the
96+
driver's ISR. However on the ESP32, there is an edge case where the REC will increase before the
97+
driver's ISR can respond in time (e.g., due to the rapid occurrence of bus errors), thus causing the
98+
REC to be non-zero after bus-off. A non-zero REC can prevent bus-off recovery as the bus-off recovery
99+
condition is that both TEC and REC become 0. Enabling this option will add a workaround in the driver
100+
to forcibly reset REC to zero on reaching bus-off.
101+
102+
config TWAI_ERRATA_FIX_TX_INTR_LOST
103+
bool "Add SW workaround for TX interrupt lost errata"
104+
depends on IDF_TARGET_ESP32
105+
default n
106+
help
107+
On the ESP32, when a transmit interrupt occurs, and interrupt register is read on the same APB clock
108+
cycle, the transmit interrupt could be lost. Enabling this option will add a workaround that checks the
109+
transmit buffer status bit to recover any lost transmit interrupt.
110+
111+
config TWAI_ERRATA_FIX_RX_FRAME_INVALID
112+
bool "Add SW workaround for invalid RX frame errata"
113+
depends on IDF_TARGET_ESP32
114+
default n
115+
help
116+
On the ESP32, when receiving a data or remote frame, if a bus error occurs in the data or CRC field,
117+
the data of the next received frame could be invalid. Enabling this option will add a workaround that
118+
will reset the peripheral on detection of this errata condition. Note that if a frame is transmitted on
119+
the bus whilst the reset is ongoing, the message will not be receive by the peripheral sent on the bus
120+
during the reset, the message will be lost.
121+
122+
config TWAI_ERRATA_FIX_RX_FIFO_CORRUPT
123+
bool "Add SW workaround for RX FIFO corruption errata"
124+
depends on IDF_TARGET_ESP32
125+
default n
126+
help
127+
On the ESP32, when the RX FIFO overruns and the RX message counter maxes out at 64 messages, the entire
128+
RX FIFO is no longer recoverable. Enabling this option will add a workaround that resets the peripheral
129+
on detection of this errata condition. Note that if a frame is being sent on the bus during the reset
130+
bus during the reset, the message will be lost.
131+
90132
endmenu # TWAI Configuration
91133

92134
menu "UART configuration"

components/driver/include/driver/twai.h

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,25 @@ extern "C" {
5353
* @note The TWAI_ALERT_AND_LOG flag is not an actual alert, but will configure
5454
* the TWAI driver to log to UART when an enabled alert occurs.
5555
*/
56-
#define TWAI_ALERT_TX_IDLE 0x0001 /**< Alert(1): No more messages to transmit */
57-
#define TWAI_ALERT_TX_SUCCESS 0x0002 /**< Alert(2): The previous transmission was successful */
58-
#define TWAI_ALERT_BELOW_ERR_WARN 0x0004 /**< Alert(4): Both error counters have dropped below error warning limit */
59-
#define TWAI_ALERT_ERR_ACTIVE 0x0008 /**< Alert(8): TWAI controller has become error active */
60-
#define TWAI_ALERT_RECOVERY_IN_PROGRESS 0x0010 /**< Alert(16): TWAI controller is undergoing bus recovery */
61-
#define TWAI_ALERT_BUS_RECOVERED 0x0020 /**< Alert(32): TWAI controller has successfully completed bus recovery */
62-
#define TWAI_ALERT_ARB_LOST 0x0040 /**< Alert(64): The previous transmission lost arbitration */
63-
#define TWAI_ALERT_ABOVE_ERR_WARN 0x0080 /**< Alert(128): One of the error counters have exceeded the error warning limit */
64-
#define TWAI_ALERT_BUS_ERROR 0x0100 /**< Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus */
65-
#define TWAI_ALERT_TX_FAILED 0x0200 /**< Alert(512): The previous transmission has failed (for single shot transmission) */
66-
#define TWAI_ALERT_RX_QUEUE_FULL 0x0400 /**< Alert(1024): The RX queue is full causing a frame to be lost */
67-
#define TWAI_ALERT_ERR_PASS 0x0800 /**< Alert(2048): TWAI controller has become error passive */
68-
#define TWAI_ALERT_BUS_OFF 0x1000 /**< Alert(4096): Bus-off condition occurred. TWAI controller can no longer influence bus */
69-
#define TWAI_ALERT_ALL 0x1FFF /**< Bit mask to enable all alerts during configuration */
70-
#define TWAI_ALERT_NONE 0x0000 /**< Bit mask to disable all alerts during configuration */
71-
#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). */
56+
#define TWAI_ALERT_TX_IDLE 0x00000001 /**< Alert(1): No more messages to transmit */
57+
#define TWAI_ALERT_TX_SUCCESS 0x00000002 /**< Alert(2): The previous transmission was successful */
58+
#define TWAI_ALERT_BELOW_ERR_WARN 0x00000004 /**< Alert(4): Both error counters have dropped below error warning limit */
59+
#define TWAI_ALERT_ERR_ACTIVE 0x00000008 /**< Alert(8): TWAI controller has become error active */
60+
#define TWAI_ALERT_RECOVERY_IN_PROGRESS 0x00000010 /**< Alert(16): TWAI controller is undergoing bus recovery */
61+
#define TWAI_ALERT_BUS_RECOVERED 0x00000020 /**< Alert(32): TWAI controller has successfully completed bus recovery */
62+
#define TWAI_ALERT_ARB_LOST 0x00000040 /**< Alert(64): The previous transmission lost arbitration */
63+
#define TWAI_ALERT_ABOVE_ERR_WARN 0x00000080 /**< Alert(128): One of the error counters have exceeded the error warning limit */
64+
#define TWAI_ALERT_BUS_ERROR 0x00000100 /**< Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus */
65+
#define TWAI_ALERT_TX_FAILED 0x00000200 /**< Alert(512): The previous transmission has failed (for single shot transmission) */
66+
#define TWAI_ALERT_RX_QUEUE_FULL 0x00000400 /**< Alert(1024): The RX queue is full causing a frame to be lost */
67+
#define TWAI_ALERT_ERR_PASS 0x00000800 /**< Alert(2048): TWAI controller has become error passive */
68+
#define TWAI_ALERT_BUS_OFF 0x00001000 /**< Alert(4096): Bus-off condition occurred. TWAI controller can no longer influence bus */
69+
#define TWAI_ALERT_RX_FIFO_OVERRUN 0x00002000 /**< Alert(8192): An RX FIFO overrun has occurred */
70+
#define TWAI_ALERT_TX_RETRIED 0x00004000 /**< Alert(16384): An message transmission was cancelled and retried due to an errata workaround */
71+
#define TWAI_ALERT_PERIPH_RESET 0x00008000 /**< Alert(32768): The TWAI controller was reset */
72+
#define TWAI_ALERT_ALL 0x0000FFFF /**< Bit mask to enable all alerts during configuration */
73+
#define TWAI_ALERT_NONE 0x00000000 /**< Bit mask to disable all alerts during configuration */
74+
#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). */
7275

7376
/** @endcond */
7477

@@ -114,7 +117,8 @@ typedef struct {
114117
uint32_t tx_error_counter; /**< Current value of Transmit Error Counter */
115118
uint32_t rx_error_counter; /**< Current value of Receive Error Counter */
116119
uint32_t tx_failed_count; /**< Number of messages that failed transmissions */
117-
uint32_t rx_missed_count; /**< Number of messages that were lost due to a full RX queue */
120+
uint32_t rx_missed_count; /**< Number of messages that were lost due to a full RX queue (or errata workaround if enabled) */
121+
uint32_t rx_overrun_count; /**< Number of messages that were lost due to a RX FIFO overrun */
118122
uint32_t arb_lost_count; /**< Number of instances arbitration was lost */
119123
uint32_t bus_error_count; /**< Number of instances a bus error has occurred */
120124
} twai_status_info_t;

components/driver/linker.lf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
[mapping:driver]
3+
archive: libdriver.a
4+
entries:
5+
# TWAI workarounds that require periph_module_reset() won't work if cache is disabled due to the use of switch jump
6+
# tables in periph_module_reset(). We prevent any part of periph_module_reset() (either text or RO data) from being
7+
# placed in flash.
8+
if TWAI_ISR_IN_IRAM = y && (TWAI_ERRATA_FIX_RX_FRAME_INVALID = y || TWAI_ERRATA_FIX_RX_FIFO_CORRUPT = y):
9+
periph_ctrl: periph_module_reset (noflash)

components/driver/periph_ctrl.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414
#include "freertos/FreeRTOS.h"
1515
#include "hal/clk_gate_ll.h"
16+
#include "esp_attr.h"
1617
#include "driver/periph_ctrl.h"
1718

1819
static portMUX_TYPE periph_spinlock = portMUX_INITIALIZER_UNLOCKED;

components/driver/twai.c

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
#include "soc/soc_caps.h"
1615

1716
#include "sdkconfig.h"
1817
#include "freertos/FreeRTOS.h"
@@ -28,6 +27,7 @@
2827
#include "driver/gpio.h"
2928
#include "driver/periph_ctrl.h"
3029
#include "driver/twai.h"
30+
#include "soc/soc_caps.h"
3131
#include "soc/twai_periph.h"
3232
#include "hal/twai_hal.h"
3333
#include "esp_rom_gpio.h"
@@ -69,6 +69,7 @@ typedef struct {
6969
twai_state_t state;
7070
twai_mode_t mode;
7171
uint32_t rx_missed_count;
72+
uint32_t rx_overrun_count;
7273
uint32_t tx_failed_count;
7374
uint32_t arb_lost_count;
7475
uint32_t bus_error_count;
@@ -128,19 +129,49 @@ TWAI_ISR_ATTR static void twai_alert_handler(uint32_t alert_code, int *alert_req
128129

129130
static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req)
130131
{
132+
#ifdef SOC_TWAI_SUPPORTS_RX_STATUS
131133
uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
132134

133135
for (uint32_t i = 0; i < msg_count; i++) {
134136
twai_hal_frame_t frame;
135-
twai_hal_read_rx_buffer_and_clear(&twai_context, &frame);
136-
//Copy frame into RX Queue
137-
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
138-
p_twai_obj->rx_msg_count++;
137+
if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) {
138+
//Valid frame copied from RX buffer
139+
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
140+
p_twai_obj->rx_msg_count++;
141+
} else { //Failed to send to queue
142+
p_twai_obj->rx_missed_count++;
143+
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
144+
}
145+
} else { //Failed to read from RX buffer because message is overrun
146+
p_twai_obj->rx_overrun_count++;
147+
twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
148+
}
149+
}
150+
#else //SOC_TWAI_SUPPORTS_RX_STATUS
151+
uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
152+
bool overrun = false;
153+
//Clear all valid RX frames
154+
for (int i = 0; i < msg_count; i++) {
155+
twai_hal_frame_t frame;
156+
if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) {
157+
//Valid frame copied from RX buffer
158+
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
159+
p_twai_obj->rx_msg_count++;
160+
} else {
161+
p_twai_obj->rx_missed_count++;
162+
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
163+
}
139164
} else {
140-
p_twai_obj->rx_missed_count++;
141-
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
165+
overrun = true;
166+
break;
142167
}
143168
}
169+
//All remaining frames are treated as overrun. Clear them all
170+
if (overrun) {
171+
p_twai_obj->rx_overrun_count += twai_hal_clear_rx_fifo_overrun(&twai_context);
172+
twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
173+
}
174+
#endif //SOC_TWAI_SUPPORTS_RX_STATUS
144175
}
145176

146177
static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req)
@@ -176,56 +207,65 @@ TWAI_ISR_ATTR static void twai_intr_handler_main(void *arg)
176207
{
177208
BaseType_t task_woken = pdFALSE;
178209
int alert_req = 0;
179-
uint32_t event;
210+
uint32_t events;
180211
TWAI_ENTER_CRITICAL_ISR();
181-
if (p_twai_obj == NULL) { //Incase intr occurs whilst driver is being uninstalled
212+
if (p_twai_obj == NULL) { //In case intr occurs whilst driver is being uninstalled
182213
TWAI_EXIT_CRITICAL_ISR();
183214
return;
184215
}
185-
event = twai_hal_decode_interrupt_events(&twai_context);
186-
if (event & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
216+
events = twai_hal_get_events(&twai_context); //Get the events that triggered the interrupt
217+
218+
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
219+
if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) {
220+
twai_hal_prepare_for_reset(&twai_context);
221+
periph_module_reset(PERIPH_TWAI_MODULE);
222+
twai_hal_recover_from_reset(&twai_context);
223+
p_twai_obj->rx_missed_count += twai_hal_get_reset_lost_rx_cnt(&twai_context);
224+
twai_alert_handler(TWAI_ALERT_PERIPH_RESET, &alert_req);
225+
}
226+
#endif
227+
if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
228+
//Note: This event will never occur if there is a periph reset event
187229
twai_handle_rx_buffer_frames(&task_woken, &alert_req);
188230
}
189-
//TX command should be the last command related handler to be called, so that
190-
//other command register bits do not overwrite the TX command bit.
191-
if (event & TWAI_HAL_EVENT_TX_BUFF_FREE) {
231+
if (events & TWAI_HAL_EVENT_TX_BUFF_FREE) {
192232
twai_handle_tx_buffer_frame(&task_woken, &alert_req);
193233
}
194234

195235
//Handle events that only require alerting (i.e. no handler)
196-
if (event & TWAI_HAL_EVENT_BUS_OFF) {
236+
if (events & TWAI_HAL_EVENT_BUS_OFF) {
197237
p_twai_obj->state = TWAI_STATE_BUS_OFF;
198238
twai_alert_handler(TWAI_ALERT_BUS_OFF, &alert_req);
199239
}
200-
if (event & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
240+
if (events & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
201241
p_twai_obj->state = TWAI_STATE_STOPPED;
202242
twai_alert_handler(TWAI_ALERT_BUS_RECOVERED, &alert_req);
203243
}
204-
if (event & TWAI_HAL_EVENT_BUS_ERR) {
244+
if (events & TWAI_HAL_EVENT_BUS_ERR) {
205245
p_twai_obj->bus_error_count++;
206246
twai_alert_handler(TWAI_ALERT_BUS_ERROR, &alert_req);
207247
}
208-
if (event & TWAI_HAL_EVENT_ARB_LOST) {
248+
if (events & TWAI_HAL_EVENT_ARB_LOST) {
209249
p_twai_obj->arb_lost_count++;
210250
twai_alert_handler(TWAI_ALERT_ARB_LOST, &alert_req);
211251
}
212-
if (event & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) {
252+
if (events & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) {
213253
//Bus-recovery in progress. TEC has dropped below error warning limit
214254
twai_alert_handler(TWAI_ALERT_RECOVERY_IN_PROGRESS, &alert_req);
215255
}
216-
if (event & TWAI_HAL_EVENT_ERROR_PASSIVE) {
256+
if (events & TWAI_HAL_EVENT_ERROR_PASSIVE) {
217257
//Entered error passive
218258
twai_alert_handler(TWAI_ALERT_ERR_PASS, &alert_req);
219259
}
220-
if (event & TWAI_HAL_EVENT_ERROR_ACTIVE) {
260+
if (events & TWAI_HAL_EVENT_ERROR_ACTIVE) {
221261
//Returned to error active
222262
twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, &alert_req);
223263
}
224-
if (event & TWAI_HAL_EVENT_ABOVE_EWL) {
264+
if (events & TWAI_HAL_EVENT_ABOVE_EWL) {
225265
//TEC or REC surpassed error warning limit
226266
twai_alert_handler(TWAI_ALERT_ABOVE_ERR_WARN, &alert_req);
227267
}
228-
if (event & TWAI_HAL_EVENT_BELOW_EWL) {
268+
if (events & TWAI_HAL_EVENT_BELOW_EWL) {
229269
//TEC and REC are both below error warning
230270
twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, &alert_req);
231271
}
@@ -646,6 +686,7 @@ esp_err_t twai_get_status_info(twai_status_info_t *status_info)
646686
status_info->msgs_to_rx = p_twai_obj->rx_msg_count;
647687
status_info->tx_failed_count = p_twai_obj->tx_failed_count;
648688
status_info->rx_missed_count = p_twai_obj->rx_missed_count;
689+
status_info->rx_overrun_count = p_twai_obj->rx_overrun_count;
649690
status_info->arb_lost_count = p_twai_obj->arb_lost_count;
650691
status_info->bus_error_count = p_twai_obj->bus_error_count;
651692
status_info->state = p_twai_obj->state;

components/hal/esp32/include/hal/can_hal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ static inline bool can_hal_check_state_flags(can_hal_context_t *hal_ctx, uint32_
109109
/* ----------------------------- Event Handling ----------------------------- */
110110

111111
static inline uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx) {
112-
return twai_hal_decode_interrupt_events(hal_ctx);
112+
return twai_hal_decode_interrupt(hal_ctx);
113113
}
114114

115115
/* ------------------------------- TX and RX -------------------------------- */

0 commit comments

Comments
 (0)