mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-09 04:25:32 +00:00
components/esp32: add inter-processor call API and implement spi_flash through it
With this change, flash operations can run on both cores. NVS and WiFi stack can also run in dual core mode now.
This commit is contained in:
@@ -36,6 +36,8 @@
|
||||
#include "esp_spi_flash.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_ipc.h"
|
||||
|
||||
static void IRAM_ATTR user_start_cpu0(void);
|
||||
static void IRAM_ATTR call_user_start_cpu1();
|
||||
@@ -180,37 +182,39 @@ void IRAM_ATTR call_user_start_cpu1() {
|
||||
user_start_cpu1();
|
||||
}
|
||||
|
||||
extern volatile int port_xSchedulerRunning;
|
||||
extern int xPortStartScheduler();
|
||||
extern volatile int port_xSchedulerRunning[2];
|
||||
|
||||
void user_start_cpu1(void) {
|
||||
//Wait for the freertos initialization is finished on CPU0
|
||||
while (port_xSchedulerRunning == 0) ;
|
||||
ets_printf("Core0 started initializing FreeRTOS. Jumping to scheduler.\n");
|
||||
//Okay, start the scheduler!
|
||||
void IRAM_ATTR user_start_cpu1(void) {
|
||||
// Wait for FreeRTOS initialization to finish on PRO CPU
|
||||
while (port_xSchedulerRunning[0] == 0) {
|
||||
;
|
||||
}
|
||||
ets_printf("Starting scheduler on APP CPU.\n");
|
||||
// Start the scheduler on APP CPU
|
||||
xPortStartScheduler();
|
||||
}
|
||||
|
||||
extern void (*__init_array_start)(void);
|
||||
extern void (*__init_array_end)(void);
|
||||
|
||||
extern esp_err_t app_main();
|
||||
static void do_global_ctors(void) {
|
||||
void (**p)(void);
|
||||
for(p = &__init_array_start; p != &__init_array_end; ++p)
|
||||
(*p)();
|
||||
}
|
||||
|
||||
extern esp_err_t app_main();
|
||||
|
||||
void user_start_cpu0(void) {
|
||||
ets_setup_syscalls();
|
||||
do_global_ctors();
|
||||
esp_ipc_init();
|
||||
spi_flash_init();
|
||||
|
||||
#if CONFIG_WIFI_ENABLED
|
||||
ets_printf("nvs_flash_init\n");
|
||||
esp_err_t ret = nvs_flash_init(5, 3);
|
||||
if (ret != ESP_OK) {
|
||||
ets_printf("nvs_flash_init fail, ret=%d\n", ret);
|
||||
printf("nvs_flash_init failed, ret=%d\n", ret);
|
||||
}
|
||||
|
||||
system_init();
|
||||
@@ -227,6 +231,7 @@ void user_start_cpu0(void) {
|
||||
app_main();
|
||||
#endif
|
||||
|
||||
ets_printf("Starting scheduler on PRO CPU.\n");
|
||||
vTaskStartScheduler();
|
||||
}
|
||||
|
||||
|
@@ -27,7 +27,10 @@ typedef int32_t esp_err_t;
|
||||
#define ESP_OK 0
|
||||
#define ESP_FAIL -1
|
||||
|
||||
#define ESP_ERR_NO_MEM 0x101
|
||||
#define ESP_ERR_NO_MEM 0x101
|
||||
#define ESP_ERR_INVALID_ARG 0x102
|
||||
#define ESP_ERR_INVALID_STATE 0x103
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
84
components/esp32/include/esp_ipc.h
Normal file
84
components/esp32/include/esp_ipc.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef __ESP_IPC_H__
|
||||
#define __ESP_IPC_H__
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
typedef void (*esp_ipc_func_t)(void* arg);
|
||||
|
||||
/**
|
||||
* @brief Inter-processor call APIs
|
||||
*
|
||||
* FreeRTOS provides several APIs which can be used to communicate between
|
||||
* different tasks, including tasks running on different CPUs.
|
||||
* This module provides additional APIs to run some code on the other CPU.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize inter-processor call module.
|
||||
*
|
||||
* This function start two tasks, one on each CPU. These tasks are started
|
||||
* with high priority. These tasks are normally inactive, waiting until one of
|
||||
* the esp_ipc_call_* functions to be used. One of these tasks will be
|
||||
* woken up to execute the callback provided to esp_ipc_call_nonblocking or
|
||||
* esp_ipc_call_blocking.
|
||||
*/
|
||||
void esp_ipc_init();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Execute function on the given CPU
|
||||
*
|
||||
* This will wake a high-priority task on CPU indicated by cpu_id argument,
|
||||
* and run func(arg) in the context of that task.
|
||||
* This function returns as soon as the high-priority task is woken up.
|
||||
* If another IPC call is already being executed, this function will also wait
|
||||
* for it to complete.
|
||||
*
|
||||
* In single-core mode, returns ESP_ERR_INVALID_ARG for cpu_id 1.
|
||||
*
|
||||
* @param cpu_id CPU where function should be executed (0 or 1)
|
||||
* @param func pointer to a function which should be executed
|
||||
* @param arg arbitrary argument to be passed into function
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if cpu_id is invalid
|
||||
* ESP_OK otherwise
|
||||
*/
|
||||
esp_err_t esp_ipc_call(uint32_t cpu_id, esp_ipc_func_t func, void* arg);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Execute function on the given CPU and wait for it to finish
|
||||
*
|
||||
* This will wake a high-priority task on CPU indicated by cpu_id argument,
|
||||
* and run func(arg) in the context of that task.
|
||||
* This function waits for func to return.
|
||||
*
|
||||
* In single-core mode, returns ESP_ERR_INVALID_ARG for cpu_id 1.
|
||||
*
|
||||
* @param cpu_id CPU where function should be executed (0 or 1)
|
||||
* @param func pointer to a function which should be executed
|
||||
* @param arg arbitrary argument to be passed into function
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if cpu_id is invalid
|
||||
* ESP_OK otherwise
|
||||
*/
|
||||
esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void* arg);
|
||||
|
||||
|
||||
|
||||
#endif /* __ESP_IPC_H__ */
|
117
components/esp32/ipc.c
Normal file
117
components/esp32/ipc.c
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_ipc.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
|
||||
static TaskHandle_t s_ipc_tasks[portNUM_PROCESSORS]; // Two high priority tasks, one for each CPU
|
||||
static SemaphoreHandle_t s_ipc_mutex; // This mutex is used as a global lock for esp_ipc_* APIs
|
||||
static SemaphoreHandle_t s_ipc_sem[portNUM_PROCESSORS]; // Two semaphores used to wake each of s_ipc_tasks
|
||||
static SemaphoreHandle_t s_ipc_ack; // Semaphore used to acknowledge that task was woken up,
|
||||
// or function has finished running
|
||||
static volatile esp_ipc_func_t s_func; // Function which should be called by high priority task
|
||||
static void * volatile s_func_arg; // Argument to pass into s_func
|
||||
typedef enum {
|
||||
IPC_WAIT_FOR_START,
|
||||
IPC_WAIT_FOR_END
|
||||
} esp_ipc_wait_t;
|
||||
|
||||
static volatile esp_ipc_wait_t s_ipc_wait; // This variable tells high priority task when it should give
|
||||
// s_ipc_ack semaphore: before s_func is called, or
|
||||
// after it returns
|
||||
|
||||
static void IRAM_ATTR ipc_task(void* arg)
|
||||
{
|
||||
const uint32_t cpuid = (uint32_t) arg;
|
||||
assert(cpuid == xPortGetCoreID());
|
||||
while (true) {
|
||||
// Wait for IPC to be initiated.
|
||||
// This will be indicated by giving the semaphore corresponding to
|
||||
// this CPU.
|
||||
if (xSemaphoreTake(s_ipc_sem[cpuid], portMAX_DELAY) != pdTRUE) {
|
||||
// TODO: when can this happen?
|
||||
abort();
|
||||
}
|
||||
|
||||
esp_ipc_func_t func = s_func;
|
||||
void* arg = s_func_arg;
|
||||
|
||||
if (s_ipc_wait == IPC_WAIT_FOR_START) {
|
||||
xSemaphoreGive(s_ipc_ack);
|
||||
}
|
||||
(*func)(arg);
|
||||
if (s_ipc_wait == IPC_WAIT_FOR_END) {
|
||||
xSemaphoreGive(s_ipc_ack);
|
||||
}
|
||||
}
|
||||
// TODO: currently this is unreachable code. Introduce esp_ipc_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
|
||||
// IPC yet.
|
||||
// Also need to delete the semaphore here.
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void esp_ipc_init()
|
||||
{
|
||||
s_ipc_mutex = xSemaphoreCreateMutex();
|
||||
s_ipc_ack = xSemaphoreCreateBinary();
|
||||
const char* task_names[2] = {"ipc0", "ipc1"};
|
||||
for (int i = 0; i < portNUM_PROCESSORS; ++i) {
|
||||
s_ipc_sem[i] = xSemaphoreCreateBinary();
|
||||
xTaskCreatePinnedToCore(ipc_task, task_names[i], XT_STACK_MIN_SIZE, (void*) i,
|
||||
configMAX_PRIORITIES - 1, &s_ipc_tasks[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_ipc_call_and_wait(uint32_t cpu_id, esp_ipc_func_t func, void* arg, esp_ipc_wait_t wait_for)
|
||||
{
|
||||
if (cpu_id >= portNUM_PROCESSORS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
xSemaphoreTake(s_ipc_mutex, portMAX_DELAY);
|
||||
|
||||
s_func = func;
|
||||
s_func_arg = arg;
|
||||
s_ipc_wait = IPC_WAIT_FOR_START;
|
||||
xSemaphoreGive(s_ipc_sem[cpu_id]);
|
||||
xSemaphoreTake(s_ipc_ack, portMAX_DELAY);
|
||||
xSemaphoreGive(s_ipc_mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_ipc_call(uint32_t cpu_id, esp_ipc_func_t func, void* arg)
|
||||
{
|
||||
return esp_ipc_call_and_wait(cpu_id, func, arg, IPC_WAIT_FOR_START);
|
||||
}
|
||||
|
||||
esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void* arg)
|
||||
{
|
||||
return esp_ipc_call_and_wait(cpu_id, func, arg, IPC_WAIT_FOR_END);
|
||||
}
|
||||
|
Reference in New Issue
Block a user