[SOLVED] Avoid blocking code execution (erase flash) for TWAI

leschge
Posts: 37
Joined: Fri May 06, 2022 1:38 pm

[SOLVED] Avoid blocking code execution (erase flash) for TWAI

Postby leschge » Thu Mar 28, 2024 10:13 am

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
Last edited by leschge on Fri Apr 05, 2024 11:34 am, edited 2 times in total.

ESP_Sprite
Posts: 9757
Joined: Thu Nov 26, 2015 4:08 am

Re: Check if internal flash is busy

Postby ESP_Sprite » Fri Mar 29, 2024 2:05 am

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?

leschge
Posts: 37
Joined: Fri May 06, 2022 1:38 pm

Re: Check if internal flash is busy

Postby leschge » Tue Apr 02, 2024 12:58 pm

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

DrMickeyLauer
Posts: 168
Joined: Sun May 22, 2022 2:42 pm

Re: Check if internal flash is busy

Postby DrMickeyLauer » Tue Apr 02, 2024 2:15 pm

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?

leschge
Posts: 37
Joined: Fri May 06, 2022 1:38 pm

Re: Check if internal flash is busy

Postby leschge » Tue Apr 02, 2024 2:45 pm

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?
Yes we do have PSRAM and do actually cache the messages in a buffer, before we process them.
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.

DrMickeyLauer
Posts: 168
Joined: Sun May 22, 2022 2:42 pm

Re: Check if internal flash is busy

Postby DrMickeyLauer » Tue Apr 02, 2024 3:31 pm

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

leschge
Posts: 37
Joined: Fri May 06, 2022 1:38 pm

Re: Check if internal flash is busy

Postby leschge » Wed Apr 03, 2024 10:26 am

DrMickeyLauer 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.)
The code is not currently set to be in IRAM, I will try this. Thanks

leschge
Posts: 37
Joined: Fri May 06, 2022 1:38 pm

Re: Check if internal flash is busy

Postby leschge » Thu Apr 04, 2024 7:43 am

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.

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);
The code that blocks this task from running is esp_partition_erase_range().

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");
Prints, as seen the erase operation takes around 24 ms.

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

DrMickeyLauer
Posts: 168
Joined: Sun May 22, 2022 2:42 pm

Re: Avoid blocking code execution (erase flash)

Postby DrMickeyLauer » Thu Apr 04, 2024 10:03 am

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

MicroController
Posts: 1725
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Avoid blocking code execution (erase flash)

Postby MicroController » Fri Apr 05, 2024 10:16 am

Instead of

Code: Select all

  err = twai_receive(&message, 0);
  ...
  vTaskDelay(2/portTICK_PERIOD_MS);
try something like

Code: Select all

  err = twai_receive(&message, 1000/portTICK_PERIOD_MS);
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'.

Who is online

Users browsing this forum: No registered users and 485 guests