kopia lustrzana https://github.com/espressif/esp-idf
components/spi_flash: make spi_flash APIs dual-core-compatible
See esp_spi_flash.c for implementation notes.pull/21/head
rodzic
29c2e58c75
commit
dec846a953
|
@ -21,6 +21,194 @@
|
|||
#include <rom/cache.h>
|
||||
#include <esp_attr.h>
|
||||
#include <soc/dport_reg.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
Driver for SPI flash read/write/erase operations
|
||||
|
||||
In order to perform some flash operations, we need to make sure both CPUs
|
||||
are not running any code from flash for the duration of the flash operation.
|
||||
In a single-core setup this is easy: we disable interrupts/scheduler and do
|
||||
the flash operation. In the dual-core setup this is slightly more complicated.
|
||||
We need to make sure that the other CPU doesn't run any code from flash.
|
||||
|
||||
SPI flash driver starts two tasks (spi_flash_op_block_task), one pinned to
|
||||
each CPU. Each task is associated with its own semaphore.
|
||||
|
||||
When SPI flash API is called on CPU A (can be PRO or APP), we wake up the task
|
||||
on CPU B by "giving" the semaphore associated with it. Tasks resumes, disables
|
||||
cache on CPU B, and acknowledges that it has taken the semaphore by setting
|
||||
a flag (s_flash_op_can_start). Flash API function running on CPU A waits for
|
||||
this flag to be set. Once the flag is set, it disables cache on CPU A and
|
||||
starts flash operation.
|
||||
|
||||
While flash operation is running, interrupts can still run on CPU B.
|
||||
We assume that all interrupt code is placed into RAM.
|
||||
|
||||
Once flash operation is complete, function on CPU A sets another flag,
|
||||
s_flash_op_complete, to let the task on CPU B know that it can re-enable
|
||||
cache and release the CPU. Then the function on CPU A re-enables the cache on
|
||||
CPU A as well and returns control to the calling code. Task on CPU B returns
|
||||
to suspended state by "taking" the semaphore.
|
||||
|
||||
Additionally, all API functions are protected with a mutex (s_flash_op_mutex).
|
||||
|
||||
In a single core environment (CONFIG_FREERTOS_UNICORE enabled), we simply
|
||||
disable both caches, no inter-CPU communication takes place.
|
||||
*/
|
||||
|
||||
static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc);
|
||||
extern void Cache_Flush(int);
|
||||
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
|
||||
static TaskHandle_t s_flash_op_tasks[2];
|
||||
static SemaphoreHandle_t s_flash_op_mutex;
|
||||
static SemaphoreHandle_t s_flash_op_sem[2];
|
||||
static bool s_flash_op_can_start = false;
|
||||
static bool s_flash_op_complete = false;
|
||||
|
||||
|
||||
// Task whose duty is to block other tasks from running on a given CPU
|
||||
static void IRAM_ATTR spi_flash_op_block_task(void* arg)
|
||||
{
|
||||
uint32_t cpuid = (uint32_t) arg;
|
||||
while (true) {
|
||||
// Wait for flash operation to be initiated.
|
||||
// This will be indicated by giving the semaphore corresponding to
|
||||
// this CPU.
|
||||
if (xSemaphoreTake(s_flash_op_sem[cpuid], portMAX_DELAY) != pdTRUE) {
|
||||
// TODO: when can this happen?
|
||||
abort();
|
||||
}
|
||||
// Disable cache on this CPU
|
||||
Cache_Read_Disable(cpuid);
|
||||
// Signal to the flash API function that flash operation can start
|
||||
s_flash_op_can_start = true;
|
||||
while (!s_flash_op_complete) {
|
||||
// until we have a way to use interrupts for inter-CPU communication,
|
||||
// busy loop here and wait for the other CPU to finish flash operation
|
||||
}
|
||||
// Flash operation is complete, re-enable cache
|
||||
Cache_Read_Enable(cpuid);
|
||||
}
|
||||
// TODO: currently this is unreachable code. Introduce spi_flash_uninit
|
||||
// function which will signal to both tasks that they can shut down.
|
||||
// Not critical at this point, we don't have a use case for stopping
|
||||
// SPI flash driver yet.
|
||||
// Also need to delete the semaphore here.
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void spi_flash_init()
|
||||
{
|
||||
s_flash_op_can_start = false;
|
||||
s_flash_op_complete = false;
|
||||
s_flash_op_sem[0] = xSemaphoreCreateBinary();
|
||||
s_flash_op_sem[1] = xSemaphoreCreateBinary();
|
||||
s_flash_op_mutex = xSemaphoreCreateMutex();
|
||||
// Start two tasks, one on each CPU, with max priorities
|
||||
// TODO: optimize stack usage. Stack size 512 is too small.
|
||||
xTaskCreatePinnedToCore(spi_flash_op_block_task, "flash_op_pro", 1024, (void*) 0,
|
||||
configMAX_PRIORITIES - 1, &s_flash_op_tasks[0], 0);
|
||||
xTaskCreatePinnedToCore(spi_flash_op_block_task, "flash_op_app", 1024, (void*) 1,
|
||||
configMAX_PRIORITIES - 1, &s_flash_op_tasks[1], 1);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
|
||||
{
|
||||
// Take the API lock
|
||||
xSemaphoreTake(s_flash_op_mutex, portMAX_DELAY);
|
||||
const uint32_t cpuid = xPortGetCoreID();
|
||||
uint32_t other_cpuid = !cpuid;
|
||||
s_flash_op_can_start = false;
|
||||
s_flash_op_complete = false;
|
||||
// Signal to the spi_flash_op_block_task on the other CPU that we need it to
|
||||
// disable cache there and block other tasks from executing.
|
||||
xSemaphoreGive(s_flash_op_sem[other_cpuid]);
|
||||
while (!s_flash_op_can_start) {
|
||||
// Busy loop and wait for spi_flash_op_block_task to take the semaphore on the
|
||||
// other CPU.
|
||||
}
|
||||
vTaskSuspendAll();
|
||||
// Disable cache on this CPU as well
|
||||
Cache_Read_Disable(cpuid);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
|
||||
{
|
||||
uint32_t cpuid = xPortGetCoreID();
|
||||
// Signal to spi_flash_op_block_task that flash operation is complete
|
||||
s_flash_op_complete = true;
|
||||
// Re-enable cache on this CPU
|
||||
Cache_Read_Enable(cpuid);
|
||||
// Release API lock
|
||||
xSemaphoreGive(s_flash_op_mutex);
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
#else // CONFIG_FREERTOS_UNICORE
|
||||
|
||||
void spi_flash_init()
|
||||
{
|
||||
// No-op in single core mode
|
||||
}
|
||||
|
||||
static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
|
||||
{
|
||||
vTaskSuspendAll();
|
||||
Cache_Read_Disable(0);
|
||||
Cache_Read_Disable(1);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
|
||||
{
|
||||
Cache_Read_Enable(0);
|
||||
Cache_Read_Enable(1);
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
#endif // CONFIG_FREERTOS_UNICORE
|
||||
|
||||
|
||||
esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec)
|
||||
{
|
||||
spi_flash_disable_interrupts_caches_and_other_cpu();
|
||||
SpiFlashOpResult rc;
|
||||
rc = SPIUnlock();
|
||||
if (rc == SPI_FLASH_RESULT_OK) {
|
||||
rc = SPIEraseSector(sec);
|
||||
}
|
||||
Cache_Flush(0);
|
||||
Cache_Flush(1);
|
||||
spi_flash_enable_interrupts_caches_and_other_cpu();
|
||||
return spi_flash_translate_rc(rc);
|
||||
}
|
||||
|
||||
esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size)
|
||||
{
|
||||
spi_flash_disable_interrupts_caches_and_other_cpu();
|
||||
SpiFlashOpResult rc;
|
||||
rc = SPIUnlock();
|
||||
if (rc == SPI_FLASH_RESULT_OK) {
|
||||
rc = SPIWrite(dest_addr, src, (int32_t) size);
|
||||
}
|
||||
Cache_Flush(0);
|
||||
Cache_Flush(1);
|
||||
spi_flash_enable_interrupts_caches_and_other_cpu();
|
||||
return spi_flash_translate_rc(rc);
|
||||
}
|
||||
|
||||
esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size)
|
||||
{
|
||||
spi_flash_disable_interrupts_caches_and_other_cpu();
|
||||
SpiFlashOpResult rc;
|
||||
rc = SPIRead(src_addr, dest, (int32_t) size);
|
||||
Cache_Flush(0);
|
||||
Cache_Flush(1);
|
||||
spi_flash_enable_interrupts_caches_and_other_cpu();
|
||||
return spi_flash_translate_rc(rc);
|
||||
}
|
||||
|
||||
static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc)
|
||||
{
|
||||
|
@ -34,73 +222,3 @@ static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc)
|
|||
return ESP_ERR_FLASH_OP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
extern void Cache_Flush(int);
|
||||
|
||||
static void IRAM_ATTR spi_flash_disable_interrupts_caches_and_app_cpu()
|
||||
{
|
||||
vTaskSuspendAll();
|
||||
// SET_PERI_REG_MASK(APPCPU_CTRL_REG_C, DPORT_APPCPU_RUNSTALL);
|
||||
Cache_Read_Disable(0);
|
||||
Cache_Read_Disable(1);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR spi_flash_enable_interrupts_caches_and_app_cpu()
|
||||
{
|
||||
Cache_Read_Enable(0);
|
||||
Cache_Read_Enable(1);
|
||||
// CLEAR_PERI_REG_MASK(APPCPU_CTRL_REG_C, DPORT_APPCPU_RUNSTALL);
|
||||
xTaskResumeAll();
|
||||
}
|
||||
|
||||
esp_err_t IRAM_ATTR spi_flash_erase_sector(uint16_t sec)
|
||||
{
|
||||
spi_flash_disable_interrupts_caches_and_app_cpu();
|
||||
SpiFlashOpResult rc;
|
||||
if (xPortGetCoreID() == 1) {
|
||||
rc = SPI_FLASH_RESULT_ERR;
|
||||
}
|
||||
else {
|
||||
rc = SPIUnlock();
|
||||
if (rc == SPI_FLASH_RESULT_OK) {
|
||||
rc = SPIEraseSector(sec);
|
||||
}
|
||||
}
|
||||
Cache_Flush(0);
|
||||
Cache_Flush(1);
|
||||
spi_flash_enable_interrupts_caches_and_app_cpu();
|
||||
return spi_flash_translate_rc(rc);
|
||||
}
|
||||
|
||||
esp_err_t IRAM_ATTR spi_flash_write(uint32_t dest_addr, const uint32_t *src, uint32_t size)
|
||||
{
|
||||
spi_flash_disable_interrupts_caches_and_app_cpu();
|
||||
SpiFlashOpResult rc;
|
||||
if (xPortGetCoreID() == 1) {
|
||||
rc = SPI_FLASH_RESULT_ERR;
|
||||
}
|
||||
else {
|
||||
rc = SPIUnlock();
|
||||
if (rc == SPI_FLASH_RESULT_OK) {
|
||||
rc = SPIWrite(dest_addr, src, (int32_t) size);
|
||||
}
|
||||
}
|
||||
Cache_Flush(0);
|
||||
Cache_Flush(1);
|
||||
spi_flash_enable_interrupts_caches_and_app_cpu();
|
||||
return spi_flash_translate_rc(rc);
|
||||
}
|
||||
|
||||
esp_err_t IRAM_ATTR spi_flash_read(uint32_t src_addr, uint32_t *dest, uint32_t size)
|
||||
{
|
||||
spi_flash_disable_interrupts_caches_and_app_cpu();
|
||||
SpiFlashOpResult rc;
|
||||
if (xPortGetCoreID() == 1) {
|
||||
rc = SPI_FLASH_RESULT_ERR;
|
||||
}
|
||||
else {
|
||||
rc = SPIRead(src_addr, dest, (int32_t) size);
|
||||
}
|
||||
spi_flash_enable_interrupts_caches_and_app_cpu();
|
||||
return spi_flash_translate_rc(rc);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,15 @@ extern "C" {
|
|||
|
||||
#define SPI_FLASH_SEC_SIZE 4096 /**< SPI Flash sector size */
|
||||
|
||||
/**
|
||||
* @brief Initialize SPI flash access driver
|
||||
*
|
||||
* This function must be called exactly once, before any other
|
||||
* spi_flash_* functions are called.
|
||||
*
|
||||
*/
|
||||
void spi_flash_init();
|
||||
|
||||
/**
|
||||
* @brief Erase the Flash sector.
|
||||
*
|
||||
|
|
Ładowanie…
Reference in New Issue