xtensa/esp32s3: Enable SMP support

Signed-off-by: Gustavo Henrique Nihei <gustavo.nihei@espressif.com>
This commit is contained in:
Gustavo Henrique Nihei 2022-03-08 14:03:25 -03:00 committed by Xiang Xiao
parent 4888be37e3
commit 4a29fa903b
16 changed files with 1032 additions and 78 deletions

View file

@ -52,6 +52,10 @@ ifeq ($(CONFIG_SPINLOCK),y)
CMN_CSRCS += xtensa_testset.c
endif
ifeq ($(CONFIG_SMP),y)
CMN_CSRCS += xtensa_cpupause.c
endif
ifeq ($(CONFIG_STACK_COLORATION),y)
CMN_CSRCS += xtensa_checkstack.c
endif
@ -73,6 +77,11 @@ ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y)
CHIP_CSRCS += esp32s3_idle.c
endif
ifeq ($(CONFIG_SMP),y)
CHIP_ASRCS = esp32s3_cpuindex.S
CHIP_CSRCS += esp32s3_cpuidlestack.c esp32s3_cpustart.c esp32s3_intercpu_interrupt.c
endif
ifeq ($(CONFIG_SCHED_TICKLESS),y)
CHIP_CSRCS += esp32s3_tickless.c
else

View file

@ -27,6 +27,12 @@
#include <nuttx/config.h>
#ifndef __ASSEMBLY__
#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
#include <stdint.h>
#endif
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@ -41,6 +47,14 @@
* Public Data
****************************************************************************/
#ifdef __ASSEMBLY__
#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
.global g_cpu_intstack_top
#endif /* CONFIG_SMP && CONFIG_ARCH_INTERRUPTSTACK > 15 */
#endif /* __ASSEMBLY__ */
/****************************************************************************
* Assembly Language Macros
****************************************************************************/
@ -48,22 +62,34 @@
#ifdef __ASSEMBLY__
/* Macro to get the current core ID. Only uses the reg given as an argument.
* Reading PRID on the ESP108 architecture gives us 0xcdcd on the PRO
* processor and 0xabab on the APP CPU. We distinguish between the two by
* simply checking bit 1: it's 1 on the APP and 0 on the PRO processor.
* Reading PRID on the ESP32 gives us 0xCDCD on the PRO processor (0)
* and 0xABAB on the APP CPU (1). We can distinguish between the two by
* checking bit 13: it's 1 on the APP and 0 on the PRO processor.
*/
.macro getcoreid reg
rsr.prid \reg
bbci \reg, 1, 1f
movi \reg, 1
j 2f
1:
movi \reg, 0
2:
.macro getcoreid reg
rsr.prid \reg
extui \reg,\reg,13,1
.endm
#endif /* __ASSEMBLY */
/****************************************************************************
* Name: setintstack
*
* Description:
* Set the current stack pointer to the "top" of the correct interrupt
* stack for the current CPU.
*
****************************************************************************/
#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
.macro setintstack tmp1 tmp2
getcoreid \tmp1 /* tmp1 = Core ID (0 or 1) */
movi \tmp2, g_cpu_intstack_top /* tmp2 = Array of stack pointers */
addx4 \tmp2, \tmp1, \tmp2 /* tmp2 = tmp2 + (tmp1 << 2) */
l32i a1, \tmp2, 0 /* a1 = *tmp2 */
.endm
#endif
#endif /* __ASSEMBLY__ */
/****************************************************************************
* Public Data
@ -82,6 +108,11 @@ extern "C"
* Public Functions Prototypes
****************************************************************************/
#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
uintptr_t xtensa_intstack_alloc(void);
uintptr_t xtensa_intstack_top(void);
#endif
#undef EXTERN
#if defined(__cplusplus)
}

View file

@ -0,0 +1,106 @@
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_cpuidlestack.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include "xtensa.h"
#include "esp32s3_smp.h"
#ifdef CONFIG_SMP
/****************************************************************************
* Public Data
****************************************************************************/
/* Address of the CPU1 IDLE thread */
uint32_t g_cpu1_idlestack[CPU1_IDLETHREAD_STACKWORDS]
aligned_data(16) locate_data(".noinit");
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_cpu_idlestack
*
* Description:
* Allocate a stack for the CPU[n] IDLE task (n > 0) if appropriate and
* setup up stack-related information in the IDLE task's TCB. This
* function is always called before up_cpu_start(). This function is
* only called for the CPU's initial IDLE task; up_create_task is used for
* all normal tasks, pthreads, and kernel threads for all CPUs.
*
* The initial IDLE task is a special case because the CPUs can be started
* in different wans in different environments:
*
* 1. The CPU may already have been started and waiting in a low power
* state for up_cpu_start(). In this case, the IDLE thread's stack
* has already been allocated and is already in use. Here
* up_cpu_idlestack() only has to provide information about the
* already allocated stack.
*
* 2. The CPU may be disabled but started when up_cpu_start() is called.
* In this case, a new stack will need to be created for the IDLE
* thread and this function is then equivalent to:
*
* return up_create_stack(tcb, stack_size, TCB_FLAG_TTYPE_KERNEL);
*
* The following TCB fields must be initialized by this function:
*
* - adj_stack_size: Stack size after adjustment for hardware, processor,
* etc. This value is retained only for debug purposes.
* - stack_alloc_ptr: Pointer to allocated stack
* - stack_base_ptr: Adjusted stack base pointer after the TLS Data and
* Arguments has been removed from the stack allocation.
*
* Input Parameters:
* - cpu: CPU index that indicates which CPU the IDLE task is
* being created for.
* - tcb: The TCB of new CPU IDLE task
* - stack_size: The requested stack size for the IDLE task. At least
* this much must be allocated. This should be
* CONFIG_IDLETHREAD_STACKSIZE.
*
****************************************************************************/
int up_cpu_idlestack(int cpu, struct tcb_s *tcb, size_t stack_size)
{
/* XTENSA uses a push-down stack: the stack grows toward lower* addresses
* in memory. The stack pointer register points to the lowest, valid
* working address (the "top" of the stack). Items on the stack are
* referenced as positive word offsets from sp.
*/
/* Save information about pre-allocated IDLE thread stack */
tcb->stack_alloc_ptr = g_cpu1_idlestack;
tcb->adj_stack_size = CPU1_IDLETHREAD_STACKSIZE;
tcb->stack_base_ptr = tcb->stack_alloc_ptr;
return OK;
}
#endif /* CONFIG_SMP */

View file

@ -0,0 +1,64 @@
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_cpuindex.S
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
.file "esp32s3_cpuindex.S"
/****************************************************************************
* Included Files
****************************************************************************/
#include <arch/xtensa/xtensa_abi.h>
#include "chip_macros.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_cpu_index
*
* Description:
* Return an index in the range of 0 through (CONFIG_SMP_NCPUS-1) that
* corresponds to the currently executing CPU.
*
* If TLS is enabled, then the RTOS can get this information from the TLS
* info structure. Otherwise, the MCU-specific logic must provide some
* mechanism to provide the CPU index.
*
* Input Parameters:
* None
*
* Returned Value:
* An integer index in the range of 0 through (CONFIG_SMP_NCPUS-1) that
* corresponds to the currently executing CPU.
*
****************************************************************************/
.text
.align 4
.global up_cpu_index
.type up_cpu_index, @function
up_cpu_index:
ENTRY(16)
getcoreid a2
RET(16)
.size up_cpu_index, . - up_cpu_index

View file

@ -0,0 +1,286 @@
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_cpustart.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#include <nuttx/sched_note.h>
#include <nuttx/spinlock.h>
#include <sched/sched.h>
#include "xtensa.h"
#include "esp32s3_irq.h"
#include "esp32s3_region.h"
#include "esp32s3_smp.h"
#include "hardware/esp32s3_rtccntl.h"
#include "hardware/esp32s3_system.h"
/****************************************************************************
* Private Data
****************************************************************************/
static volatile bool g_appcpu_started;
static volatile spinlock_t g_appcpu_interlock;
/****************************************************************************
* ROM function prototypes
****************************************************************************/
extern void ets_set_appcpu_boot_addr(uint32_t start);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: xtensa_attach_fromcpu0_interrupt
****************************************************************************/
static inline void xtensa_attach_fromcpu0_interrupt(void)
{
int cpuint;
/* Connect all CPU peripheral source to allocated CPU interrupt */
cpuint = esp32s3_setup_irq(1, ESP32S3_PERIPH_INT_FROM_CPU0, 1,
ESP32S3_CPUINT_LEVEL);
DEBUGASSERT(cpuint >= 0);
/* Attach the inter-CPU interrupt. */
irq_attach(ESP32S3_IRQ_INT_FROM_CPU0, (xcpt_t)esp32s3_fromcpu0_interrupt,
NULL);
/* Enable the inter 0 CPU interrupts. */
up_enable_irq(ESP32S3_IRQ_INT_FROM_CPU0);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: xtensa_appcpu_start
*
* Description:
* This is the entry point used for the APP CPU when it's started via
* up_cpu_start(). The actual start-up logic is in ROM and we boot up
* in C code.
*
* Input Parameters:
* None
*
* Returned Value:
* None, does not return
*
****************************************************************************/
void xtensa_appcpu_start(void)
{
struct tcb_s *tcb = this_task();
register uint32_t sp;
#ifdef CONFIG_STACK_COLORATION
{
register uint32_t *ptr;
register int i;
/* If stack debug is enabled, then fill the stack with a recognizable
* value that we can use later to test for high water marks.
*/
for (i = 0, ptr = (uint32_t *)tcb->stack_alloc_ptr;
i < tcb->adj_stack_size;
i += sizeof(uint32_t))
{
*ptr++ = STACK_COLOR;
}
}
#endif
/* Move to the stack assigned to us by up_smp_start immediately. Although
* we were give a stack pointer at start-up, we don't know where that stack
* pointer is positioned respect to our memory map. The only safe option
* is to switch to a well-known IDLE thread stack.
*/
sp = (uint32_t)tcb->stack_base_ptr + tcb->adj_stack_size;
__asm__ __volatile__("mov sp, %0\n" : : "r"(sp));
sinfo("CPU%d Started\n", up_cpu_index());
#ifdef CONFIG_SCHED_INSTRUMENTATION
/* Notify that this CPU has started */
sched_note_cpu_started(tcb);
#endif
/* Release the spinlock to signal to the PRO CPU that the APP CPU has
* started.
*/
g_appcpu_started = true;
spin_unlock(&g_appcpu_interlock);
/* Reset scheduler parameters */
nxsched_resume_scheduler(tcb);
/* Move CPU0 exception vectors to IRAM */
__asm__ __volatile__ ("wsr %0, vecbase\n"::"r" (&_init_start));
/* Make page 0 access raise an exception */
esp32s3_region_protection();
/* Initialize CPU interrupts */
esp32s3_cpuint_initialize();
/* Attach and enable the inter-CPU interrupt */
xtensa_attach_fromcpu0_interrupt();
/* Enable the software interrupt */
up_enable_irq(XTENSA_IRQ_SWINT);
#ifndef CONFIG_SUPPRESS_INTERRUPTS
/* And Enable interrupts */
up_irq_enable();
#endif
/* Then switch contexts. This instantiates the exception context of the
* tcb at the head of the assigned task list. In this case, this should
* be the CPUs NULL task.
*/
xtensa_context_restore(tcb->xcp.regs);
}
/****************************************************************************
* Name: up_cpu_start
*
* Description:
* In an SMP configuration, only one CPU is initially active (CPU 0).
* System initialization occurs on that single thread. At the completion of
* the initialization of the OS, just before beginning normal multitasking,
* the additional CPUs would be started by calling this function.
*
* Each CPU is provided the entry point to its IDLE task when started. A
* TCB for each CPU's IDLE task has been initialized and placed in the
* CPU's g_assignedtasks[cpu] list. No stack has been allocated or
* initialized.
*
* The OS initialization logic calls this function repeatedly until each
* CPU has been started, 1 through (CONFIG_SMP_NCPUS-1).
*
* Input Parameters:
* cpu - The index of the CPU being started. This will be a numeric
* value in the range of one to (CONFIG_SMP_NCPUS-1).
* (CPU 0 is already active)
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
int up_cpu_start(int cpu)
{
DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu());
if (!g_appcpu_started)
{
uint32_t regval;
/* Start CPU1 */
sinfo("Starting CPU%d\n", cpu);
#ifdef CONFIG_SCHED_INSTRUMENTATION
/* Notify of the start event */
sched_note_cpu_start(this_task(), cpu);
#endif
/* This spinlock will be used as a handshake between the two CPUs.
* It's first initialized to its locked state, later the PRO CPU will
* try to lock it but spins until the APP CPU starts and unlocks it.
*/
spin_initialize(&g_appcpu_interlock, SP_LOCKED);
/* Unstall the APP CPU */
regval = getreg32(RTC_CNTL_RTC_SW_CPU_STALL_REG);
regval &= ~RTC_CNTL_SW_STALL_APPCPU_C1_M;
putreg32(regval, RTC_CNTL_RTC_SW_CPU_STALL_REG);
regval = getreg32(RTC_CNTL_RTC_OPTIONS0_REG);
regval &= ~RTC_CNTL_SW_STALL_APPCPU_C0_M;
putreg32(regval, RTC_CNTL_RTC_OPTIONS0_REG);
/* Enable clock gating for the APP CPU */
regval = getreg32(SYSTEM_CORE_1_CONTROL_0_REG);
regval |= SYSTEM_CONTROL_CORE_1_CLKGATE_EN;
putreg32(regval, SYSTEM_CORE_1_CONTROL_0_REG);
regval = getreg32(SYSTEM_CORE_1_CONTROL_0_REG);
regval &= ~SYSTEM_CONTROL_CORE_1_RUNSTALL;
putreg32(regval, SYSTEM_CORE_1_CONTROL_0_REG);
/* Reset the APP CPU */
regval = getreg32(SYSTEM_CORE_1_CONTROL_0_REG);
regval |= SYSTEM_CONTROL_CORE_1_RESETING;
putreg32(regval, SYSTEM_CORE_1_CONTROL_0_REG);
regval = getreg32(SYSTEM_CORE_1_CONTROL_0_REG);
regval &= ~SYSTEM_CONTROL_CORE_1_RESETING;
putreg32(regval, SYSTEM_CORE_1_CONTROL_0_REG);
/* Set the CPU1 start address */
ets_set_appcpu_boot_addr((uint32_t)xtensa_appcpu_start);
/* And wait until the APP CPU starts and releases the spinlock. */
spin_lock(&g_appcpu_interlock);
DEBUGASSERT(g_appcpu_started);
}
return OK;
}

View file

@ -34,9 +34,10 @@
#include <nuttx/irq.h>
#include <nuttx/clock.h>
#include <nuttx/spinlock.h>
#include "esp32s3_freerun.h"
#include "esp32s3_clockconfig.h"
#include "esp32s3_freerun.h"
#include "esp32s3_gpio.h"
#ifdef CONFIG_ESP32S3_FREERUN
@ -59,6 +60,12 @@
#define TIMER_WIDTH 54 /* ESP32-S3 timer has 54-bit counter */
/****************************************************************************
* Private Data
****************************************************************************/
static spinlock_t g_lock; /* Device specific lock */
/****************************************************************************
* Private Functions
****************************************************************************/
@ -209,9 +216,9 @@ int esp32s3_freerun_initialize(struct esp32s3_freerun_s *freerun, int chan,
/* Register the handler */
{
irqstate_t flags = enter_critical_section();
irqstate_t flags = spin_lock_irqsave(&g_lock);
ret = ESP32S3_TIM_SETISR(freerun->tch, freerun_handler, freerun);
leave_critical_section(flags);
spin_unlock_irqrestore(&g_lock, flags);
}
if (ret == OK)
@ -266,7 +273,7 @@ int esp32s3_freerun_counter(struct esp32s3_freerun_s *freerun,
/* Temporarily disable the overflow counter. */
flags = enter_critical_section();
flags = spin_lock_irqsave(&g_lock);
overflow = freerun->overflow;
ESP32S3_TIM_GETCTR(freerun->tch, &counter);
@ -293,7 +300,7 @@ int esp32s3_freerun_counter(struct esp32s3_freerun_s *freerun,
freerun->overflow = overflow;
}
leave_critical_section(flags);
spin_unlock_irqrestore(&g_lock, flags);
tmrinfo("counter=%" PRIu64 " (%" PRIu64 ") overflow=%" PRIu32
", pending=%i\n",

View file

@ -0,0 +1,127 @@
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_intercpu_interrupt.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include <nuttx/arch.h>
#include <nuttx/spinlock.h>
#include "xtensa.h"
#include "hardware/esp32s3_system.h"
#ifdef CONFIG_SMP
/****************************************************************************
* Private Function
****************************************************************************/
/****************************************************************************
* Name: esp32s3_fromcpu_interrupt
*
* Description:
* Common logic called to handle the from CPU0/1 interrupts.
*
****************************************************************************/
static int IRAM_ATTR esp32s3_fromcpu_interrupt(int fromcpu)
{
uintptr_t regaddr;
DEBUGASSERT((unsigned)fromcpu < CONFIG_SMP_NCPUS);
DEBUGASSERT(fromcpu != up_cpu_index());
/* Clear the interrupt from the other CPU */
regaddr = (fromcpu == 0) ? SYSTEM_CPU_INTR_FROM_CPU_0_REG :
SYSTEM_CPU_INTR_FROM_CPU_1_REG;
putreg32(0, regaddr);
/* Call pause handler */
xtensa_pause_handler();
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s3_fromcpu[0,1]_interrupt
*
* Description:
* Called to handle the from CPU0/1 interrupts.
*
****************************************************************************/
int IRAM_ATTR esp32s3_fromcpu0_interrupt(int irq, void *context, void *arg)
{
return esp32s3_fromcpu_interrupt(0);
}
int IRAM_ATTR esp32s3_fromcpu1_interrupt(int irq, void *context, void *arg)
{
return esp32s3_fromcpu_interrupt(1);
}
/****************************************************************************
* Name: xtensa_intercpu_interrupt
*
* Description:
* Called to trigger a CPU interrupt
*
****************************************************************************/
int IRAM_ATTR xtensa_intercpu_interrupt(int tocpu, int intcode)
{
int fromcpu;
DEBUGASSERT((unsigned)tocpu < CONFIG_SMP_NCPUS &&
(unsigned)intcode <= UINT8_MAX);
fromcpu = up_cpu_index();
DEBUGASSERT(fromcpu != tocpu);
/* Generate an Inter-Processor Interrupt */
if (fromcpu == 0)
{
putreg32(SYSTEM_CPU_INTR_FROM_CPU_0, SYSTEM_CPU_INTR_FROM_CPU_0_REG);
}
else
{
putreg32(SYSTEM_CPU_INTR_FROM_CPU_1, SYSTEM_CPU_INTR_FROM_CPU_1_REG);
}
return OK;
}
#endif /* CONFIG_SMP */

View file

@ -38,16 +38,27 @@
#include "xtensa.h"
#include "esp32s3_irq.h"
#ifdef CONFIG_SMP
#include "esp32s3_smp.h"
#endif
#include "hardware/esp32s3_interrupt_core0.h"
#ifdef CONFIG_SMP
#include "hardware/esp32s3_interrupt_core1.h"
#endif
#include "hardware/esp32s3_soc.h"
#include "hardware/esp32s3_system.h"
#include "hardware/esp32s3_interrupt_core0.h"
#include "esp32s3_irq.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Interrupt stack definitions for SMP */
#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
# define INTSTACK_ALLOC (CONFIG_SMP_NCPUS * INTSTACK_SIZE)
#endif
/* IRQ to CPU and CPU interrupts mapping:
*
* Encoding: CIIIIIII
@ -77,6 +88,9 @@
/* Mapping Peripheral IDs to map register addresses. */
#define CORE0_MAP_REGADDR(n) (DR_REG_INTERRUPT_CORE0_BASE + ((n) << 2))
#ifdef CONFIG_SMP
# define CORE1_MAP_REGADDR(n) (DR_REG_INTERRUPT_CORE1_BASE + ((n) << 2))
#endif
/* CPU interrupts can be detached from any peripheral source by setting the
* map register to an internal CPU interrupt (6, 7, 11, 15, 16, or 29).
@ -100,7 +114,29 @@
* CURRENT_REGS for portability.
*/
volatile uint32_t *g_current_regs[1];
/* For the case of architectures with multiple CPUs, then there must be one
* such value for each processor that can receive an interrupt.
*/
volatile uint32_t *g_current_regs[CONFIG_SMP_NCPUS];
#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
/* In the SMP configuration, we will need custom interrupt stacks.
* These definitions provide the aligned stack allocations.
*/
static uint32_t g_intstackalloc[INTSTACK_ALLOC >> 2];
/* These definitions provide the "top" of the push-down stacks. */
uintptr_t g_cpu_intstack_top[CONFIG_SMP_NCPUS] =
{
(uintptr_t)g_intstackalloc + INTSTACK_SIZE,
#if CONFIG_SMP_NCPUS > 1
(uintptr_t)g_intstackalloc + (2 * INTSTACK_SIZE),
#endif /* CONFIG_SMP_NCPUS > 1 */
};
#endif /* defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15 */
/****************************************************************************
* Private Data
@ -109,6 +145,9 @@ volatile uint32_t *g_current_regs[1];
/* Maps a CPU interrupt to the IRQ of the attached peripheral interrupt */
static uint8_t g_cpu0_intmap[ESP32S3_NCPUINTS];
#ifdef CONFIG_SMP
static uint8_t g_cpu1_intmap[ESP32S3_NCPUINTS];
#endif
static volatile uint8_t g_irqmap[NR_IRQS];
@ -116,13 +155,16 @@ static volatile uint8_t g_irqmap[NR_IRQS];
* content.
*/
static uint32_t g_intenable[1];
static uint32_t g_intenable[CONFIG_SMP_NCPUS];
/* Bitsets for free, unallocated CPU interrupts available to peripheral
* devices.
*/
static uint32_t g_cpu0_freeints = ESP32S3_CPUINT_PERIPHSET;
#ifdef CONFIG_SMP
static uint32_t g_cpu1_freeints = ESP32S3_CPUINT_PERIPHSET;
#endif
/* Bitsets for each interrupt priority 1-5 */
@ -139,6 +181,32 @@ static const uint32_t g_priority[5] =
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: xtensa_attach_fromcpu1_interrupt
****************************************************************************/
#ifdef CONFIG_SMP
static inline void xtensa_attach_fromcpu1_interrupt(void)
{
int cpuint;
/* Connect all CPU peripheral source to allocated CPU interrupt */
cpuint = esp32s3_setup_irq(0, ESP32S3_PERIPH_INT_FROM_CPU1, 1,
ESP32S3_CPUINT_LEVEL);
DEBUGASSERT(cpuint >= 0);
/* Attach the inter-CPU interrupt. */
irq_attach(ESP32S3_IRQ_INT_FROM_CPU1, (xcpt_t)esp32s3_fromcpu1_interrupt,
NULL);
/* Enable the inter-CPU interrupt. */
up_enable_irq(ESP32S3_IRQ_INT_FROM_CPU1);
}
#endif
/****************************************************************************
* Name: esp32s3_intinfo
*
@ -151,8 +219,20 @@ static const uint32_t g_priority[5] =
static void esp32s3_intinfo(int cpu, int periphid,
uintptr_t *regaddr, uint8_t **intmap)
{
*regaddr = CORE0_MAP_REGADDR(periphid);
*intmap = g_cpu0_intmap;
#ifdef CONFIG_SMP
DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS);
if (cpu != 0)
{
*regaddr = CORE1_MAP_REGADDR(periphid);
*intmap = g_cpu1_intmap;
}
else
#endif
{
*regaddr = CORE0_MAP_REGADDR(periphid);
*intmap = g_cpu0_intmap;
}
}
/****************************************************************************
@ -187,7 +267,16 @@ static int esp32s3_getcpuint(uint32_t intmask)
*/
cpu = up_cpu_index();
freeints = &g_cpu0_freeints;
#ifdef CONFIG_SMP
if (cpu != 0)
{
freeints = &g_cpu1_freeints;
}
else
#endif
{
freeints = &g_cpu0_freeints;
}
intset = *freeints & intmask;
if (intset != 0)
@ -308,7 +397,16 @@ static void esp32s3_free_cpuint(int cpuint)
bitmask = 1ul << cpuint;
freeints = &g_cpu0_freeints;
#ifdef CONFIG_SMP
if (up_cpu_index() != 0)
{
freeints = &g_cpu1_freeints;
}
else
#endif
{
freeints = &g_cpu0_freeints;
}
DEBUGASSERT((*freeints & bitmask) == 0);
*freeints |= bitmask;
@ -342,6 +440,12 @@ void up_irqinitialize(void)
esp32s3_cpuint_initialize();
#ifdef CONFIG_SMP
/* Attach and enable the inter-CPU interrupt */
xtensa_attach_fromcpu1_interrupt();
#endif
#ifndef CONFIG_SUPPRESS_INTERRUPTS
/* And finally, enable interrupts. Also clears PS.EXCM */
@ -378,7 +482,7 @@ void up_disable_irq(int irq)
}
DEBUGASSERT(cpuint >= 0 && cpuint <= ESP32S3_CPUINT_MAX);
DEBUGASSERT(cpu == 0);
DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS);
if (irq < XTENSA_NIRQ_INTERNAL)
{
@ -386,6 +490,16 @@ void up_disable_irq(int irq)
* the Interrupt Matrix.
*/
#ifdef CONFIG_SMP
int me = up_cpu_index();
if (me != cpu)
{
/* It was the other CPU that enabled this interrupt. */
return;
}
#endif
xtensa_disable_cpuint(&g_intenable[cpu], 1ul << cpuint);
}
else
@ -444,7 +558,7 @@ void up_enable_irq(int irq)
int cpu = IRQ_GETCPU(g_irqmap[irq]);
DEBUGASSERT(cpu == 0);
DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS);
/* For peripheral interrupts, attach the interrupt to the peripheral;
* the CPU interrupt was already enabled when allocated.
@ -463,6 +577,36 @@ void up_enable_irq(int irq)
}
}
/****************************************************************************
* Name: xtensa_intstack_top
*
* Description:
* Return a pointer to the top of the correct interrupt stack for the
* given CPU.
*
****************************************************************************/
#if defined(CONFIG_SMP) && CONFIG_ARCH_INTERRUPTSTACK > 15
uintptr_t xtensa_intstack_top(void)
{
return g_cpu_intstack_top[up_cpu_index()];
}
/****************************************************************************
* Name: xtensa_intstack_alloc
*
* Description:
* Return a pointer to the "alloc" the correct interrupt stack allocation
* for the current CPU.
*
****************************************************************************/
uintptr_t xtensa_intstack_alloc(void)
{
return g_cpu_intstack_top[up_cpu_index()] - INTSTACK_SIZE;
}
#endif
/****************************************************************************
* Name: esp32s3_cpuint_initialize
*
@ -482,8 +626,18 @@ int esp32s3_cpuint_initialize(void)
{
uintptr_t regaddr;
uint8_t *intmap;
#ifdef CONFIG_SMP
int cpu;
#endif
int i;
#ifdef CONFIG_SMP
/* Which CPU are we initializing */
cpu = up_cpu_index();
DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS);
#endif
/* Disable all CPU interrupts on this CPU */
xtensa_disable_all();
@ -492,14 +646,32 @@ int esp32s3_cpuint_initialize(void)
for (i = 0; i < ESP32S3_NPERIPHERALS; i++)
{
regaddr = CORE0_MAP_REGADDR(i);
#ifdef CONFIG_SMP
if (cpu != 0)
{
regaddr = CORE1_MAP_REGADDR(i);
}
else
#endif
{
regaddr = CORE0_MAP_REGADDR(i);
}
putreg32(NO_CPUINT, regaddr);
}
/* Initialize CPU interrupt-to-IRQ mapping table */
intmap = g_cpu0_intmap;
#ifdef CONFIG_SMP
if (cpu != 0)
{
intmap = g_cpu1_intmap;
}
else
#endif
{
intmap = g_cpu0_intmap;
}
/* Indicate that no peripheral interrupts are assigned to CPU interrupts */
@ -665,12 +837,27 @@ uint32_t *xtensa_int_decode(uint32_t cpuints, uint32_t *regs)
uint8_t *intmap;
uint32_t mask;
int bit;
#ifdef CONFIG_SMP
int cpu;
#endif
#ifdef CONFIG_ARCH_LEDS_CPU_ACTIVITY
board_autoled_on(LED_CPU);
#endif
intmap = g_cpu0_intmap;
#ifdef CONFIG_SMP
/* Select PRO or APP CPU interrupt mapping table */
cpu = up_cpu_index();
if (cpu != 0)
{
intmap = g_cpu1_intmap;
}
else
#endif
{
intmap = g_cpu0_intmap;
}
/* Skip over zero bits, eight at a time */

View file

@ -676,12 +676,12 @@ void esp32s3_lowputc_rst_rxfifo(const struct esp32s3_uart_s *priv)
*
****************************************************************************/
void esp32s3_lowputc_disable_all_uart_int(const struct esp32s3_uart_s *priv,
void esp32s3_lowputc_disable_all_uart_int(struct esp32s3_uart_s *priv,
uint32_t *current_status)
{
irqstate_t flags;
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
if (current_status != NULL)
{
@ -698,7 +698,7 @@ void esp32s3_lowputc_disable_all_uart_int(const struct esp32s3_uart_s *priv,
putreg32(0xffffffff, UART_INT_CLR_REG(priv->id));
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
}
/****************************************************************************

View file

@ -26,24 +26,24 @@
****************************************************************************/
#include <nuttx/config.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/spinlock.h>
#include "chip.h"
#include "esp32s3_irq.h"
#include "hardware/esp32s3_uart.h"
#include "hardware/esp32s3_gpio_sigmap.h"
#include "esp32s3_irq.h"
/****************************************************************************
* Public Types
****************************************************************************/
@ -116,6 +116,7 @@ struct esp32s3_uart_s
uint8_t ctssig; /* CTS signal */
bool oflow; /* Output flow control (CTS) enabled */
#endif
spinlock_t lock; /* Device-specific lock */
};
extern struct esp32s3_uart_s g_uart0_config;
@ -424,7 +425,7 @@ void esp32s3_lowputc_rst_rxfifo(const struct esp32s3_uart_s *priv);
*
****************************************************************************/
void esp32s3_lowputc_disable_all_uart_int(const struct esp32s3_uart_s *priv,
void esp32s3_lowputc_disable_all_uart_int(struct esp32s3_uart_s *priv,
uint32_t *current_status);
/****************************************************************************

View file

@ -24,17 +24,18 @@
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <nuttx/arch.h>
#include <nuttx/timers/oneshot.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spinlock.h>
#include <nuttx/timers/oneshot.h>
#include "esp32s3_oneshot.h"
@ -58,7 +59,8 @@ struct esp32s3_oneshot_lowerhalf_s
struct esp32s3_oneshot_s oneshot; /* ESP32-S3-specific oneshot state */
oneshot_callback_t callback; /* Upper-half Interrupt callback */
void *arg; /* Argument passed to handler */
uint16_t resolution;
uint16_t resolution; /* Timer's resolution in microseconds */
spinlock_t lock; /* Device-specific lock */
};
/****************************************************************************
@ -210,12 +212,12 @@ static int oneshot_lh_start(struct oneshot_lowerhalf_s *lower,
/* Save the callback information and start the timer */
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
priv->callback = callback;
priv->arg = arg;
ret = esp32s3_oneshot_start(&priv->oneshot, oneshot_lh_handler,
priv, ts);
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
if (ret < 0)
{
@ -261,11 +263,11 @@ static int oneshot_lh_cancel(struct oneshot_lowerhalf_s *lower,
/* Cancel the timer */
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
ret = esp32s3_oneshot_cancel(&priv->oneshot, ts);
priv->callback = NULL;
priv->arg = NULL;
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
if (ret < 0)
{

View file

@ -533,6 +533,7 @@ static void esp32s3_txint(struct uart_dev_s *dev, bool enable)
{
struct esp32s3_uart_s *priv = dev->priv;
uint32_t ints_mask = UART_TXFIFO_EMPTY_INT_ENA_M | UART_TX_DONE_INT_ENA_M;
irqstate_t flags = spin_lock_irqsave(&priv->lock);
if (enable)
{
@ -550,6 +551,8 @@ static void esp32s3_txint(struct uart_dev_s *dev, bool enable)
modifyreg32(UART_INT_ENA_REG(priv->id), ints_mask, 0);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
/****************************************************************************
@ -569,6 +572,7 @@ static void esp32s3_rxint(struct uart_dev_s *dev, bool enable)
struct esp32s3_uart_s *priv = dev->priv;
uint32_t ints_mask = UART_RXFIFO_TOUT_INT_ENA_M |
UART_RXFIFO_FULL_INT_ENA_M;
irqstate_t flags = spin_lock_irqsave(&priv->lock);
if (enable)
{
@ -590,6 +594,8 @@ static void esp32s3_rxint(struct uart_dev_s *dev, bool enable)
modifyreg32(UART_INT_ENA_REG(priv->id), ints_mask, 0);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
/****************************************************************************

View file

@ -0,0 +1,71 @@
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_smp.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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 __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_SMP_H
#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_SMP_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#ifdef CONFIG_SMP
/****************************************************************************
* Pre-procesor Definitions
****************************************************************************/
/* An IDLE thread stack size for CPU0 must be defined */
#if !defined(CONFIG_IDLETHREAD_STACKSIZE)
# error CONFIG_IDLETHREAD_STACKSIZE is not defined
#elif CONFIG_IDLETHREAD_STACKSIZE < 16
# error CONFIG_IDLETHREAD_STACKSIZE is to small
#endif
#define CPU1_IDLETHREAD_STACKSIZE ((CONFIG_IDLETHREAD_STACKSIZE + 15) & ~15)
#define CPU1_IDLETHREAD_STACKWORDS (CPU1_IDLETHREAD_STACKSIZE >> 2)
/****************************************************************************
* Public Data
****************************************************************************/
/* This is the CPU1 IDLE stack */
extern uint32_t g_cpu1_idlestack[CPU1_IDLETHREAD_STACKWORDS];
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: esp32s3_fromcpu[0,1]_interrupt
*
* Description:
* Called to handle the from CPU0/1 interrupts.
*
****************************************************************************/
int esp32s3_fromcpu0_interrupt(int irq, void *context, void *arg);
int esp32s3_fromcpu1_interrupt(int irq, void *context, void *arg);
#endif /* CONFIG_SMP */
#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_SMP_H */

View file

@ -34,6 +34,7 @@
#include <stdio.h>
#include <nuttx/arch.h>
#include <nuttx/spinlock.h>
#include <nuttx/timers/timer.h>
#include "hardware/esp32s3_soc.h"
@ -62,6 +63,7 @@ struct esp32s3_timer_lowerhalf_s
void *arg; /* Argument passed to upper half callback */
bool started; /* True: Timer has been started */
void *upper; /* Pointer to watchdog_upperhalf_s */
spinlock_t lock; /* Device-specific lock */
};
/****************************************************************************
@ -250,9 +252,9 @@ static int timer_lh_start(struct timer_lowerhalf_s *lower)
if (priv->callback != NULL)
{
irqstate_t flags = enter_critical_section();
irqstate_t flags = spin_lock_irqsave(&priv->lock);
ret = ESP32S3_TIM_SETISR(priv->tim, timer_lh_handler, priv);
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
if (ret != OK)
{
@ -305,9 +307,9 @@ static int timer_lh_stop(struct timer_lowerhalf_s *lower)
ESP32S3_TIM_DISABLEINT(priv->tim);
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
ret = ESP32S3_TIM_SETISR(priv->tim, NULL, NULL);
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
ESP32S3_TIM_STOP(priv->tim);
@ -473,7 +475,7 @@ static void timer_lh_setcallback(struct timer_lowerhalf_s *lower,
priv->callback = callback;
priv->arg = arg;
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
/* There is a user callback and the timer has already been started */
@ -488,7 +490,7 @@ static void timer_lh_setcallback(struct timer_lowerhalf_s *lower,
ret = ESP32S3_TIM_SETISR(priv->tim, NULL, NULL);
}
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
if (ret != OK)
{

View file

@ -24,22 +24,22 @@
#include <nuttx/config.h>
#include <sys/types.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <sys/types.h>
#include <nuttx/arch.h>
#include <nuttx/clock.h>
#include <nuttx/spinlock.h>
#include <nuttx/timers/watchdog.h>
#include "xtensa.h"
#include "hardware/esp32s3_soc.h"
#include "esp32s3_wdt.h"
#include "esp32s3_wdt_lowerhalf.h"
#include "hardware/esp32s3_soc.h"
/****************************************************************************
* Pre-processor Definitions
@ -94,6 +94,7 @@ struct esp32s3_wdt_lowerhalf_s
bool started; /* True: Timer has been started */
xcpt_t handler; /* User Handler */
void *upper; /* Pointer to watchdog_upperhalf_s */
spinlock_t lock; /* Device-specific lock */
};
/****************************************************************************
@ -236,16 +237,16 @@ static int wdt_lh_start(struct watchdog_lowerhalf_s *lower)
/* Set the lower-half handler and enable interrupt */
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
ESP32S3_WDT_SETISR(priv->wdt, wdt_handler, priv);
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
ESP32S3_WDT_ENABLEINT(priv->wdt);
}
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
priv->lastreset = clock_systime_ticks();
ESP32S3_WDT_START(priv->wdt);
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
/* Lock it again */
@ -290,9 +291,9 @@ static int wdt_lh_stop(struct watchdog_lowerhalf_s *lower)
ESP32S3_WDT_DISABLEINT(priv->wdt);
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
ESP32S3_WDT_SETISR(priv->wdt, NULL, NULL);
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
}
/* Lock it again */
@ -333,10 +334,10 @@ static int wdt_lh_keepalive(struct watchdog_lowerhalf_s *lower)
/* Feed the dog and updates the lastreset variable */
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
priv->lastreset = clock_systime_ticks();
ESP32S3_WDT_FEED(priv->wdt);
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
/* Lock */
@ -540,7 +541,7 @@ static xcpt_t wdt_lh_capture(struct watchdog_lowerhalf_s *lower,
ESP32S3_WDT_UNLOCK(priv->wdt);
flags = enter_critical_section();
flags = spin_lock_irqsave(&priv->lock);
/* Save the new user handler */
@ -598,7 +599,7 @@ static xcpt_t wdt_lh_capture(struct watchdog_lowerhalf_s *lower,
}
}
leave_critical_section(flags);
spin_unlock_irqrestore(&priv->lock, flags);
ESP32S3_WDT_LOCK(priv->wdt);
return oldhandler;
}

View file

@ -0,0 +1,54 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_ARCH_LEDS is not set
# CONFIG_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
# CONFIG_NSH_CMDPARMS is not set
CONFIG_ARCH="xtensa"
CONFIG_ARCH_BOARD="esp32s3-devkit"
CONFIG_ARCH_BOARD_ESP32S3_DEVKIT=y
CONFIG_ARCH_CHIP="esp32s3"
CONFIG_ARCH_CHIP_ESP32S3=y
CONFIG_ARCH_CHIP_ESP32S3WROOM1=y
CONFIG_ARCH_INTERRUPTSTACK=2048
CONFIG_ARCH_STACKDUMP=y
CONFIG_ARCH_XTENSA=y
CONFIG_BOARD_LOOPSPERMSEC=16717
CONFIG_BUILTIN=y
CONFIG_DEBUG_FULLOPT=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_ESP32S3_UART0=y
CONFIG_FS_PROCFS=y
CONFIG_HAVE_CXX=y
CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_IDLETHREAD_STACKSIZE=3072
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_LINELEN=64
CONFIG_NSH_READLINE=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=114688
CONFIG_RAM_START=0x20000000
CONFIG_RAW_BINARY=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_WAITPID=y
CONFIG_SMP=y
CONFIG_SMP_NCPUS=2
CONFIG_STACK_COLORATION=y
CONFIG_START_DAY=8
CONFIG_START_MONTH=3
CONFIG_START_YEAR=2022
CONFIG_SYSTEM_NSH=y
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_OSTEST=y
CONFIG_TESTING_SMP=y
CONFIG_UART0_SERIAL_CONSOLE=y