[SOLVED] Avoid blocking code execution (erase flash) for TWAI
[SOLVED] Avoid blocking code execution (erase flash) for TWAI
Hey,
we have some time critical code that requires to perform writes to flash memory in chunks. However, this fails as soon as another component operates on the flash as well (e.g. if esp_partition_erase_range() or esp_partition_write() is performed).
So my question is, if it is somehow possible to check in a non-blocking way if flash is currently ready to be written at.
I could wrap all the esp flash functions with my own logic, but I would like to avoid this if possible.
My Idea was to use the pins connected to internal flash memory on the ESP32 S3 to poll a busy state. But I am not if any of these pins could be used for that - if even possible.
The doc states these pins for the ESP32 S3:
26 — — SPICS1, GPIO26
27 — — SPIHD, GPIO27
28 — — SPIWP, GPIO28
29 — — SPICS0, GPIO29put only
30 — — SPICLK, GPIO30
31 — — SPIQ, GPIO31
32 — — SPID, GPIO32
Thanks
we have some time critical code that requires to perform writes to flash memory in chunks. However, this fails as soon as another component operates on the flash as well (e.g. if esp_partition_erase_range() or esp_partition_write() is performed).
So my question is, if it is somehow possible to check in a non-blocking way if flash is currently ready to be written at.
I could wrap all the esp flash functions with my own logic, but I would like to avoid this if possible.
My Idea was to use the pins connected to internal flash memory on the ESP32 S3 to poll a busy state. But I am not if any of these pins could be used for that - if even possible.
The doc states these pins for the ESP32 S3:
26 — — SPICS1, GPIO26
27 — — SPIHD, GPIO27
28 — — SPIWP, GPIO28
29 — — SPICS0, GPIO29put only
30 — — SPICLK, GPIO30
31 — — SPIQ, GPIO31
32 — — SPID, GPIO32
Thanks
Last edited by leschge on Fri Apr 05, 2024 11:34 am, edited 2 times in total.
-
- Posts: 9757
- Joined: Thu Nov 26, 2015 4:08 am
Re: Check if internal flash is busy
That's technically not possible. When writing to flash, code executing from the flash cache isn't available and as such all but the very highest-priority task will be suspended. That should include other tasks that are about to do something to flash.
Any chance you can post the error and/or your code?
Any chance you can post the error and/or your code?
Re: Check if internal flash is busy
We use the TWAI lib to update the ESP32 OTA partition, if in the meantime another task (also on other core) accesses the flash memory, it results in a TWAI_ALERT_RX_FIFO_OVERRUN. We could increase the FrameSeperationTime to ease up the bus load, but would increase update time by a lot.
I found this ticket which sounds very similar:
https://github.com/espressif/esp-idf/issues/11238
I found this ticket which sounds very similar:
https://github.com/espressif/esp-idf/issues/11238
-
- Posts: 168
- Joined: Sun May 22, 2022 2:42 pm
Re: Check if internal flash is busy
Due to the tiny internal buffer, you have to serve twai efficiently – however why don‘t you write to a memory buffer first? Do you have PSRAM?
Re: Check if internal flash is busy
Yes we do have PSRAM and do actually cache the messages in a buffer, before we process them.DrMickeyLauer wrote: Due to the tiny internal buffer, you have to serve twai efficiently – however why don‘t you write to a memory buffer first? Do you have PSRAM?
Maybe I have to check why the task that polls the twai buffer seems to starve long enough to trigger an overrun, even though it has the highest priority already.
-
- Posts: 168
- Joined: Sun May 22, 2022 2:42 pm
Re: Check if internal flash is busy
I reckon you already have set the TWAI routine to IRAM and did register the GPIO service routine with a sufficiently high priority? (I have struggled a long time with the internal CAN controller to get reliable timings and then gave up and added an external one.)
Re: Check if internal flash is busy
The code is not currently set to be in IRAM, I will try this. ThanksDrMickeyLauer wrote: I reckon you already have set the TWAI routine to IRAM and did register the GPIO service routine with a sufficiently high priority? (I have struggled a long time with the internal CAN controller to get reliable timings and then gave up and added an external one.)
Re: Check if internal flash is busy
Unfortunately, I am still facing issues.
I created a simple FIFO to read the twai messages. Own task, functions marked as IRAM, runs every 2 ms, highest priority.
The code that blocks this task from running is esp_partition_erase_range().
Prints, as seen the erase operation takes around 24 ms.
I created a simple FIFO to read the twai messages. Own task, functions marked as IRAM, runs every 2 ms, highest priority.
Code: Select all
// Check if the FIFO buffer is full
int IRAM_ATTR twai_fifo_full() {
return twai_fifo.count == FIFO_SIZE;
}
// Add a message to the FIFO buffer
int IRAM_ATTR twai_fifo_push(twai_message_t *message) {
if (twai_fifo_full())
return -1; // Buffer overflow
twai_fifo.messages[twai_fifo.head] = *message;
twai_fifo.head = (twai_fifo.head + 1) % FIFO_SIZE;
twai_fifo.count++;
return 0;
}
int64_t time_prev = 0;
// Function to receive a message and store it in the FIFO buffer
void IRAM_ATTR twai_msg_cache(void* pvParameters) {
while(1)
{
twai_message_t message;
int err;
int64_t diff = esp_timer_get_time()-time_prev;
if(diff > 2500)
{
printf("diff %lld\n", diff);
}
time_prev = esp_timer_get_time();
if(CAN_TWAI_STATE_RUNNING == CAN1_getCanState()) // also marked with IRAM_ATTR
{
err = twai_receive(&message, 0);
if (err == 0) {
//printf("%02X\n",message.data[0]);
twai_fifo_push(&message);
}
}
vTaskDelay(2/portTICK_PERIOD_MS);
}
}
// Function to read a message from the FIFO buffer
int twai_msg_read(twai_message_t *message) {
return twai_fifo_pop(message);
}
Code: Select all
TaskHandle_t taskCanFifo_handle = xTaskCreateStaticPinnedToCore(twai_msg_cache, "CanFifo", 4096u, NULL, 21, taskFifoStack, &Task_FIFO, 1);
Code: Select all
if( (NULL_PTR != data) && (size <= MAX_DATA_LENGTH_IN_BLOCK) )
{
if(0 == head_dr % MAX_DATA_RECORDS_IN_SPI_FLASH_SEC)
{
printf("er\n");
/* Erase the block before start writing the data */
opStatus |= esp_partition_erase_range(partition,(head_dr * DATA_BLOCK_SIZE),partition->erase_size);
}
printf("wr\n");
/* Step 2: write data record begin pattern */
opStatus |= esp_partition_write(partition,(head_dr * DATA_BLOCK_SIZE),&dr_begin_pattern,DATA_RECORD_PATTERN_SIZE);
/* Step 3: write data record data size */
opStatus |= esp_partition_write(partition,(head_dr * DATA_BLOCK_SIZE) + DATA_RECORD_PATTERN_SIZE,&size,DATA_RECORD_DATA_SIZE);
/* Step 4: write data record payload */
opStatus |= esp_partition_write(partition,( (head_dr * DATA_BLOCK_SIZE) + DATA_RECORD_PATTERN_SIZE + DATA_RECORD_DATA_SIZE ),data,size);
/* Step 5: We should write end pattern on the data record stored in
* the previous data block to make sure we would find the correct head position at startup after SW RST */
if(0 != head_dr)
{
opStatus |= esp_partition_write(partition,( ((head_dr * DATA_BLOCK_SIZE) - DATA_BLOCK_SIZE) + DATA_RECORD_PATTERN_SIZE + DATA_RECORD_DATA_SIZE + MAX_DATA_LENGTH_IN_BLOCK),&dr_end_pattern,DATA_RECORD_PATTERN_SIZE);
}
else
{
if( (DATA_RECORD_BEGIN_PATTERN == pointerToData[last_block_in_partition].begin_pattern) && (DATA_RECORD_END_PATTERN != pointerToData[last_block_in_partition].end_pattern) )
{
opStatus |= esp_partition_write(partition,( ((last_block_in_partition * DATA_BLOCK_SIZE) - DATA_BLOCK_SIZE) + DATA_RECORD_PATTERN_SIZE + DATA_RECORD_DATA_SIZE + MAX_DATA_LENGTH_IN_BLOCK),&dr_end_pattern,DATA_RECORD_PATTERN_SIZE);
}
}
printf("done\n");
Code: Select all
size 250
wr
done
diff 2583
size 250
er
diff 24767
wr
done
diff 2509
diff 2561
size 250
er
diff 24216
wr
diff 2640
done
diff 2509
-
- Posts: 168
- Joined: Sun May 22, 2022 2:42 pm
Re: Avoid blocking code execution (erase flash)
I'm not sure I entirely understand your code, but: Here's a way to serve TWAI efficiently:
1. CONFIG_TWAI_ISR_IN_IRAM=y
2. When calling twai_driver_install(), the intr_flags member of twai_general_config_t should set the ESP_INTR_FLAG_IRAM set.
3. Create a mid-high priority TWAI receiver task that blocks in twai_receive until cancelled. Upon receiving a frame, put it into a FreeRTOS queue with sufficient number of elements (I use 558 to accommodate for a complete ISOTP frame). Put this code in IRAM.
4. In your "normal" code, have a task that blocks on the FreeRTOS queue forever and handles the arriving frames.
5. Don't print anything other than, say, an accumulated number of frames arriving per second. Printing is SLOW
1. CONFIG_TWAI_ISR_IN_IRAM=y
2. When calling twai_driver_install(), the intr_flags member of twai_general_config_t should set the ESP_INTR_FLAG_IRAM set.
3. Create a mid-high priority TWAI receiver task that blocks in twai_receive until cancelled. Upon receiving a frame, put it into a FreeRTOS queue with sufficient number of elements (I use 558 to accommodate for a complete ISOTP frame). Put this code in IRAM.
4. In your "normal" code, have a task that blocks on the FreeRTOS queue forever and handles the arriving frames.
5. Don't print anything other than, say, an accumulated number of frames arriving per second. Printing is SLOW
-
- Posts: 1725
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: Avoid blocking code execution (erase flash)
Instead of
try something like
This will give the task a chance to progress a.s.a.p. when a message is available.
And, obviously(?), limiting the size of the 'chunks' you erase/write at once can make the system more 'responsive'.
Code: Select all
err = twai_receive(&message, 0);
...
vTaskDelay(2/portTICK_PERIOD_MS);
Code: Select all
err = twai_receive(&message, 1000/portTICK_PERIOD_MS);
And, obviously(?), limiting the size of the 'chunks' you erase/write at once can make the system more 'responsive'.
Who is online
Users browsing this forum: Majestic-12 [Bot] and 407 guests