Verified Commit 3b9393fe authored by Rahix's avatar Rahix
Browse files

feat(epicardium): Implement basic core 1 lifecycle



This commit introduces a way to control core 1.  This is archieved by a
predefined API-Interrupt:  The reset interrupt.  When triggered, it will
bring the core back into its default state and wait for a new vector
address from Epicardium.  Once this vector address is transferred, it
will start the new payload.

This method only works as long as core 1 is responsive to the API
interrupts.  Cases where this might not be the case:

  - During times where core 1 has interrupts disabled
  - When in a higher priority exception handler
  - When core 1 has corrupted its IVT

Signed-off-by: Rahix's avatarRahix <rahix@rahix.de>
parent bf88fd75
......@@ -8,7 +8,8 @@
* Semaphore used for API synchronization.
* TODO: Replace this with a LDREX/STREX based implementation
*/
#define _API_SEMAPHORE 0
#define _API_SEMAPHORE 0
#define _CONTROL_SEMAPHORE 1
/* Type of API IDs */
typedef uint32_t api_id_t;
......@@ -19,6 +20,13 @@ typedef uint32_t api_id_t;
/* Layout of the shared memory for API calls */
struct api_call_mem {
/*
* Reset stub. The reset stub is a small function provided by
* epicardium that should be called by a payload when receiving the
* reset interrupt.
*/
void (*reset_stub)();
/*
* Flag for synchronization of API calls. When this flag
* is set, the caller has issued a call and is waiting for
......
#include "epicardium.h"
#include "api/dispatcher.h"
#include "api/interrupt-sender.h"
#include "modules/log.h"
#include "card10.h"
#include "max32665.h"
#include "sema.h"
#include "tmr.h"
static void __core1_init(void);
struct core1_info {
/* Location of core1's interrupt vector table */
volatile uintptr_t ivt_addr;
/* Whether core 1 is ready for a new IVT */
volatile bool ready;
};
/*
* Information passing structure for controlling core 1.
*/
static volatile struct core1_info core1_info = {
.ivt_addr = 0x00,
.ready = false,
};
/*
* Minimal IVT needed for initial startup. This IVT only contains the initial
* stack pointer and reset-handler and is used to startup core 1. Afterwards,
* the payload's IVT is loaded into VTOR and used from then on.
*/
static uintptr_t core1_initial_ivt[] = {
/* Initial Stack Pointer */
0x20080000,
/* Reset Handler */
(uintptr_t)__core1_reset,
};
/*
* Reset Handler
*
* Calls __core1_init() to reset & prepare the core for loading a new payload.
*/
__attribute__((naked)) void __core1_reset(void)
{
__asm volatile("mov sp, %0\n\t"
: /* No Outputs */
: "r"(core1_initial_ivt[0]));
__core1_init();
}
/*
* Init core 1. This function will reset the core and wait for a new IVT
* address from Epicardium. Once this address is received, it will start
* execution with the supplied reset handler.
*/
void __core1_init(void)
{
/*
* Clear any pending API interrupts.
*/
TMR_IntClear(MXC_TMR5);
/*
* Reset Interrupts
*
* To ensure proper operation of the new payload, disable all interrupts
* and clear all pending ones.
*/
for (int i = 0; i < MXC_IRQ_EXT_COUNT; i++) {
NVIC_DisableIRQ(i);
NVIC_ClearPendingIRQ(i);
NVIC_SetPriority(i, 0);
}
/*
* Check whether we catched the core during an interrupt. If this is
* the case, try returning from the exception handler first and call
* __core1_reset() again in thread context.
*/
if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0) {
/*
* Construct an exception frame so the CPU will jump back to our
* __core1_reset() function once we exit from the exception
* handler.
*
* To exit the exception, a special "EXC_RETURN" value is loaded
* into the link register and then branched to.
*/
__asm volatile(
"ldr r0, =0x41000000\n\t"
"ldr r1, =0\n\t"
"push { r0 }\n\t" /* xPSR */
"push { %0 }\n\t" /* PC */
"push { %0 }\n\t" /* LR */
"push { r1 }\n\t" /* R12 */
"push { r1 }\n\t" /* R3 */
"push { r1 }\n\t" /* R2 */
"push { r1 }\n\t" /* R1 */
"push { r1 }\n\t" /* R0 */
"ldr lr, =0xFFFFFFF9\n\t"
"bx lr\n\t"
: /* No Outputs */
: "r"((uintptr_t)__core1_reset)
: "pc", "lr");
/* unreachable */
while (1)
;
}
/* Wait for the IVT address */
while (1) {
while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
}
__DMB();
__ISB();
/*
* The IVT address is reset to 0 by Epicardium before execution
* gets here. Once a new address has been set, core 1 can use
* the new IVT.
*/
if (core1_info.ivt_addr != 0x00) {
break;
}
/* Signal that we are ready for an IVT address */
core1_info.ready = true;
SEMA_FreeSema(_CONTROL_SEMAPHORE);
__WFE();
}
uintptr_t *ivt = (uintptr_t *)core1_info.ivt_addr;
core1_info.ivt_addr = 0x00;
SEMA_FreeSema(_CONTROL_SEMAPHORE);
/*
* Reset the call-flag before entering the payload so API calls behave
* properly. This is necessary because epic_exec() will set the flag
* to "returning" on exit.
*/
API_CALL_MEM->call_flag = _API_FLAG_IDLE;
/*
* Set the IVT
*/
SCB->VTOR = (uintptr_t)ivt;
/*
* Clear any pending API interrupts.
*/
TMR_IntClear(MXC_TMR5);
NVIC_ClearPendingIRQ(TMR5_IRQn);
/*
* Jump to payload's reset handler
*/
__asm volatile(
"ldr r0, %0\n\t"
"blx r0\n\r"
: /* No Outputs */
: "m"(*(ivt + 1))
: "r0");
}
void core1_boot(void)
{
/*
* Boot using the initial IVT. This will place core 1 into a loop,
* waiting for a payload.
*/
core1_start(&core1_initial_ivt);
}
void core1_reset(void)
{
/* Signal core 1 that we intend to load a new payload. */
api_interrupt_trigger(EPIC_INT_RESET);
/* Wait for the core to accept */
while (1) {
while (SEMA_GetSema(_CONTROL_SEMAPHORE) == E_BUSY) {
}
/*
* core 1 will set the ready flag once it is spinning in the
* above loop, waiting for a new IVT.
*/
if (core1_info.ready) {
break;
}
SEMA_FreeSema(_CONTROL_SEMAPHORE);
for (int i = 0; i < 10000; i++) {
}
}
/*
* TODO: If the other core does not respond within a certain grace
* period, we need to force it into our desired state by overwriting
* all of its memory. Yes, I don't like this method either ...
*/
}
void core1_load(void *ivt, char *args)
{
/* If the core is currently in an API call, reset it. */
API_CALL_MEM->call_flag = _API_FLAG_IDLE;
API_CALL_MEM->id = 0;
API_CALL_MEM->int_id = (-1);
api_prepare_args(args);
core1_info.ivt_addr = (uintptr_t)ivt;
core1_info.ready = false;
__DMB();
__ISB();
SEMA_FreeSema(_CONTROL_SEMAPHORE);
__SEV();
__WFE();
}
......@@ -15,8 +15,12 @@ int api_dispatcher_init()
{
int ret;
ret = SEMA_Init(NULL);
API_CALL_MEM->call_flag = _API_FLAG_IDLE;
ret = SEMA_Init(NULL);
SEMA_FreeSema(_API_SEMAPHORE);
API_CALL_MEM->reset_stub = __core1_reset;
API_CALL_MEM->call_flag = _API_FLAG_IDLE;
API_CALL_MEM->id = 0;
API_CALL_MEM->int_id = (-1);
/*
* Enable TX events for both cores.
......
......@@ -28,3 +28,19 @@ api_id_t api_dispatcher_exec();
* The data is a NULL-terminated string.
*/
void api_prepare_args(char *args);
/*********************************************************************
* core 1 control *
*********************************************************************/
/* Startup core1 into a state where it is ready to receive a payload. */
void core1_boot(void);
/* Reset core 1 into a state where it can accept a new payload */
void core1_reset(void);
/* Load a payload into core 1 */
void core1_load(void *ivt, char *args);
/* core 1 reset stub. See epicardium/api/control.c for details. */
void __core1_reset(void);
......@@ -12,3 +12,10 @@ void TMR5_IRQHandler(void)
__dispatch_isr(API_CALL_MEM->int_id);
API_CALL_MEM->int_id = (-1);
}
/* Reset Handler */
void __epic_isr_reset(void)
{
API_CALL_MEM->int_id = (-1);
API_CALL_MEM->reset_stub();
}
......@@ -148,6 +148,8 @@ int main(void)
LOG_INFO("startup", "starting light sensor ...");
epic_light_sensor_run();
core1_boot();
/*
* See if there's a l0dable.elf to run. If not, run pycardium.
* This is temporary until epicardium gets a l0dable API from pycardium.
......@@ -163,11 +165,11 @@ int main(void)
LOG_INFO(
"startup", "Starting %s on core1 ...", l0dable
);
core1_start(info.isr_vector);
core1_load(info.isr_vector, "");
}
} else {
LOG_INFO("startup", "Starting pycardium on core1 ...");
core1_start((void *)0x10080000);
LOG_INFO("startup", "Starting pycardium on core 1 ...");
core1_load((void *)0x10080000, "main.py");
}
LOG_INFO("startup", "Starting FreeRTOS ...");
......
......@@ -37,8 +37,9 @@ api_dispatcher_lib = static_library(
'api-dispatcher',
'api/dispatcher.c',
'api/interrupt-sender.c',
'api/control.c',
api[1], # Dispatcher
dependencies: periphdriver,
dependencies: [libcard10, periphdriver],
)
##########################################################################
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment