You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/01/10 14:06:11 UTC
[incubator-nuttx] branch master updated: Feature k210 smp (#71)
This is an automated email from the ASF dual-hosted git repository.
gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 29d3ed2 Feature k210 smp (#71)
29d3ed2 is described below
commit 29d3ed2ec14428c19a23a34c10292cf94e2426e8
Author: Masayuki Ishikawa <ma...@gmail.com>
AuthorDate: Fri Jan 10 08:04:41 2020 -0600
Feature k210 smp (#71)
* arch: risc-v: Remove unused typedef for irqstate_t
NOTE: irqstate_t is defined in arch/risc-v/include/types.h
* arch: risc-v: Add typedef irqstate_t for __LP64__
* arch: risc-v: Add SMP support to K210 (RV64GC) processor
* boards: maxi-bit: Update READMEs and add smp/defconfig
---
arch/risc-v/Kconfig | 3 +
arch/risc-v/include/irq.h | 14 -
arch/risc-v/include/k210/irq.h | 5 +
arch/risc-v/include/spinlock.h | 131 ++++++++
arch/risc-v/include/types.h | 6 +-
arch/risc-v/src/common/up_internal.h | 13 +-
arch/risc-v/src/common/up_interruptcontext.c | 6 +-
arch/risc-v/src/k210/Kconfig | 8 +
arch/risc-v/src/k210/Make.defs | 8 +-
arch/risc-v/src/k210/hardware/k210_clint.h | 1 +
arch/risc-v/src/k210/hardware/k210_memorymap.h | 4 +
.../up_initialstate.c => k210/k210_cpuidlestack.c} | 109 +++----
.../up_interruptcontext.c => k210/k210_cpuindex.c} | 44 +--
arch/risc-v/src/k210/k210_cpupause.c | 332 +++++++++++++++++++++
.../src/k210/{k210_timerisr.c => k210_cpustart.c} | 156 ++++++----
arch/risc-v/src/k210/k210_head.S | 40 ++-
arch/risc-v/src/k210/k210_irq.c | 55 +++-
arch/risc-v/src/k210/k210_irq_dispatch.c | 30 +-
arch/risc-v/src/k210/k210_memorymap.h | 6 +-
arch/risc-v/src/k210/k210_start.c | 29 +-
arch/risc-v/src/k210/k210_timerisr.c | 4 +
arch/risc-v/src/k210/up_schedulesigaction.c | 218 +++++++++++++-
arch/risc-v/src/rv64gc/up_assert.c | 74 +++--
arch/risc-v/src/rv64gc/up_blocktask.c | 4 +-
.../src/rv64gc/{up_initialstate.c => up_fault.c} | 108 +++----
arch/risc-v/src/rv64gc/up_initialstate.c | 2 +-
arch/risc-v/src/rv64gc/up_releasepending.c | 4 +-
arch/risc-v/src/rv64gc/up_reprioritizertr.c | 4 +-
arch/risc-v/src/rv64gc/up_sigdeliver.c | 71 ++++-
arch/risc-v/src/rv64gc/up_swint.c | 24 +-
.../{k210/k210_timerisr.c => rv64gc/up_testset.S} | 126 ++++----
arch/risc-v/src/rv64gc/up_unblocktask.c | 4 +-
boards/risc-v/k210/maix-bit/README-qemu.txt | 43 +++
boards/risc-v/k210/maix-bit/README.txt | 1 +
boards/risc-v/k210/maix-bit/configs/nsh/defconfig | 2 -
.../k210/maix-bit/configs/{nsh => smp}/defconfig | 21 +-
36 files changed, 1329 insertions(+), 381 deletions(-)
diff --git a/arch/risc-v/Kconfig b/arch/risc-v/Kconfig
index 18b859b..bfc4c66 100644
--- a/arch/risc-v/Kconfig
+++ b/arch/risc-v/Kconfig
@@ -19,6 +19,9 @@ config ARCH_CHIP_FE310
config ARCH_CHIP_K210
bool "Kendryte K210"
select ARCH_RV64GC
+ select ARCH_HAVE_TESTSET
+ select ARCH_HAVE_MULTICPU
+ select ARCH_GLOBAL_IRQDISABLE
---help---
Kendryte K210 processor (RISC-V 64bit core with GC extensions)
diff --git a/arch/risc-v/include/irq.h b/arch/risc-v/include/irq.h
index 4ff217e..e21321b 100644
--- a/arch/risc-v/include/irq.h
+++ b/arch/risc-v/include/irq.h
@@ -57,18 +57,4 @@
# include <arch/rv64gc/irq.h>
#endif
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-typedef uint32_t irqstate_t;
-
-/****************************************************************************
- * Public Types
- ****************************************************************************/
-
-/****************************************************************************
- * Public Variables
- ****************************************************************************/
-
#endif /* __ARCH_RISCV_INCLUDE_IRQ_H */
diff --git a/arch/risc-v/include/k210/irq.h b/arch/risc-v/include/k210/irq.h
index ffd5db8..79d72f3 100644
--- a/arch/risc-v/include/k210/irq.h
+++ b/arch/risc-v/include/k210/irq.h
@@ -51,6 +51,7 @@
/* In mie (machine interrupt enable) register */
+#define MIE_MSIE (0x1 << 3) /* Machine Software Interrupt Enable */
#define MIE_MTIE (0x1 << 7) /* Machine Timer Interrupt Enable */
#define MIE_MEIE (0x1 << 11) /* Machine External Interrupt Enable */
@@ -81,7 +82,11 @@
/* Machine Grobal External Interrupt */
+#ifdef CONFIG_K210_WITH_QEMU
+#define K210_IRQ_UART0 (K210_IRQ_MEXT + 4)
+#else
#define K210_IRQ_UART0 (K210_IRQ_MEXT + 33)
+#endif
/* Total number of IRQs */
diff --git a/arch/risc-v/include/spinlock.h b/arch/risc-v/include/spinlock.h
new file mode 100644
index 0000000..a34971b
--- /dev/null
+++ b/arch/risc-v/include/spinlock.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+ * arch/risc-v/include/spinlock.h
+ *
+ * Copyright (C) 2020 Masayuki Ishikawa. All rights reserved.
+ * Author: Masayuki Ishikawa <ma...@gmail.com>
+ *
+ * Based on arch/arm/include/armv7-m/spinlock.h
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_INCLUDE_SPINLOCK_H
+#define __ARCH_RISCV_INCLUDE_SPINLOCK_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+# include <stdint.h>
+#endif /* __ASSEMBLY__ */
+
+/* Include RISC-V architecture-specific IRQ definitions (including register
+ * save structure and up_irq_save()/up_irq_restore() functions)
+ */
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Spinlock states */
+
+#define SP_UNLOCKED 0 /* The Un-locked state */
+#define SP_LOCKED 1 /* The Locked state */
+
+/* Memory barriers for use with NuttX spinlock logic
+ *
+ * Data Memory Barrier (DMB) acts as a memory barrier. It ensures that all
+ * explicit memory accesses that appear in program order before the DMB
+ * instruction are observed before any explicit memory accesses that appear
+ * in program order after the DMB instruction. It does not affect the
+ * ordering of any other instructions executing on the processor
+ *
+ * Data Synchronization Barrier (DSB) acts as a special kind of memory
+ * barrier. No instruction in program order after this instruction executes
+ * until this instruction completes. This instruction completes when: (1) All
+ * explicit memory accesses before this instruction complete, and (2) all
+ * Cache, Branch predictor and TLB maintenance operations before this
+ * instruction complete.
+ *
+ */
+
+#define SP_DSB(n) __asm__ __volatile__ ("fence")
+#define SP_DMB(n) __asm__ __volatile__ ("fence")
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+/* The Type of a spinlock.
+ *
+ * RISC-V architecture introuced the concept of exclusive accesses to memory
+ * locations in the form of the Load-Reserved (LR) and Store-Conditional
+ * (SC) instructions. RV64 supports doubleword aligned data only but others
+ * supports word aligned data.
+ *
+ * RISC-V architecture supports fence instruction to ensure memory ordering
+ */
+
+#ifdef __LP64__
+typedef uint64_t spinlock_t;
+#else
+typedef uint32_t spinlock_t;
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_testset
+ *
+ * Description:
+ * Perform an atomic test and set operation on the provided spinlock.
+ *
+ * This function must be provided via the architecture-specific logic.
+ *
+ * Input Parameters:
+ * lock - The address of spinlock object.
+ *
+ * Returned Value:
+ * The spinlock is always locked upon return. The value of previous value
+ * of the spinlock variable is returned, either SP_LOCKED if the spinlock
+ * as previously locked (meaning that the test-and-set operation failed to
+ * obtain the lock) or SP_UNLOCKED if the spinlock was previously unlocked
+ * (meaning that we successfully obtained the lock)
+ *
+ ****************************************************************************/
+
+/* See prototype in nuttx/include/nuttx/spinlock.h */
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_INCLUDE_SPINLOCK_H */
diff --git a/arch/risc-v/include/types.h b/arch/risc-v/include/types.h
index 77e5a49..8c0df57 100644
--- a/arch/risc-v/include/types.h
+++ b/arch/risc-v/include/types.h
@@ -81,16 +81,20 @@ typedef unsigned long long _uint64_t;
typedef signed long _intptr_t;
typedef unsigned long _uintptr_t;
+
+/* This is the size of the interrupt state save returned by irqsave(). */
+
+typedef unsigned long long irqstate_t;
#else
/* A pointer is 4 bytes */
typedef signed int _intptr_t;
typedef unsigned int _uintptr_t;
-#endif
/* This is the size of the interrupt state save returned by irqsave(). */
typedef unsigned int irqstate_t;
+#endif
#endif /* __ASSEMBLY__ */
diff --git a/arch/risc-v/src/common/up_internal.h b/arch/risc-v/src/common/up_internal.h
index 3e84353..702565f 100644
--- a/arch/risc-v/src/common/up_internal.h
+++ b/arch/risc-v/src/common/up_internal.h
@@ -70,11 +70,12 @@
*/
#ifdef CONFIG_ARCH_RV64GC
-#define up_savestate(regs) up_copystate(regs, (uint64_t*)g_current_regs)
+#define up_savestate(regs) up_copystate(regs, (uint64_t*)CURRENT_REGS)
+#define up_restorestate(regs) (CURRENT_REGS = regs)
#else
#define up_savestate(regs) up_copystate(regs, (uint32_t*)g_current_regs)
-#endif
#define up_restorestate(regs) (g_current_regs = regs)
+#endif
/* Determine which (if any) console driver to use. If a console is enabled
* and no other console device is specified, then a serial console is
@@ -118,7 +119,13 @@ extern "C"
#endif
#ifdef CONFIG_ARCH_RV64GC
-EXTERN volatile uint64_t *g_current_regs;
+#ifdef CONFIG_SMP
+EXTERN volatile uint64_t *g_current_regs[CONFIG_SMP_NCPUS];
+# define CURRENT_REGS (g_current_regs[up_cpu_index()])
+#else
+EXTERN volatile uint64_t *g_current_regs[1];
+# define CURRENT_REGS (g_current_regs[0])
+#endif
EXTERN uintptr_t g_idle_topstack;
#else
EXTERN volatile uint32_t *g_current_regs;
diff --git a/arch/risc-v/src/common/up_interruptcontext.c b/arch/risc-v/src/common/up_interruptcontext.c
index 8bdc595..cdf69bf 100644
--- a/arch/risc-v/src/common/up_interruptcontext.c
+++ b/arch/risc-v/src/common/up_interruptcontext.c
@@ -66,5 +66,9 @@
bool up_interrupt_context(void)
{
- return g_current_regs != NULL;
+#ifdef CONFIG_ARCH_RV64GC
+ return CURRENT_REGS != NULL;
+#else
+ return g_current_regs != NULL;
+#endif
}
diff --git a/arch/risc-v/src/k210/Kconfig b/arch/risc-v/src/k210/Kconfig
index ed34b92..ade8871 100644
--- a/arch/risc-v/src/k210/Kconfig
+++ b/arch/risc-v/src/k210/Kconfig
@@ -26,3 +26,11 @@ config K210_UART0
select K210_UART
endmenu
+
+menu "K210 Others"
+
+config K210_WITH_QEMU
+ bool "qemu support"
+ default n
+
+endmenu
diff --git a/arch/risc-v/src/k210/Make.defs b/arch/risc-v/src/k210/Make.defs
index 08a269f..c8dd3cf 100644
--- a/arch/risc-v/src/k210/Make.defs
+++ b/arch/risc-v/src/k210/Make.defs
@@ -37,9 +37,11 @@ HEAD_ASRC = k210_vectors.S
# Specify our general Assembly files
CHIP_ASRCS = k210_head.S up_syscall.S
+CMN_ASRCS += up_testset.S
+
# Specify C code within the common directory to be included
CMN_CSRCS += up_initialize.c up_swint.c
-CMN_CSRCS += up_allocateheap.c up_createstack.c up_exit.c
+CMN_CSRCS += up_allocateheap.c up_createstack.c up_exit.c up_fault.c
CMN_CSRCS += up_assert.c up_blocktask.c up_copystate.c up_initialstate.c
CMN_CSRCS += up_interruptcontext.c up_modifyreg32.c up_puts.c
CMN_CSRCS += up_releasepending.c up_reprioritizertr.c
@@ -60,3 +62,7 @@ CHIP_CSRCS += k210_idle.c k210_irq.c k210_irq_dispatch.c
CHIP_CSRCS += k210_lowputc.c k210_serial.c
CHIP_CSRCS += k210_start.c k210_timerisr.c
+ifeq ($(CONFIG_SMP), y)
+CHIP_CSRCS += k210_cpuidlestack.c k210_cpuindex.c
+CHIP_CSRCS += k210_cpupause.c k210_cpustart.c
+endif
diff --git a/arch/risc-v/src/k210/hardware/k210_clint.h b/arch/risc-v/src/k210/hardware/k210_clint.h
index 9968c4f..c16c64a 100644
--- a/arch/risc-v/src/k210/hardware/k210_clint.h
+++ b/arch/risc-v/src/k210/hardware/k210_clint.h
@@ -37,6 +37,7 @@
* Pre-processor Definitions
****************************************************************************/
+#define K210_CLINT_MSIP (K210_CLINT_BASE + 0x0000)
#define K210_CLINT_MTIMECMP (K210_CLINT_BASE + 0x4000)
#define K210_CLINT_MTIME (K210_CLINT_BASE + 0xbff8)
diff --git a/arch/risc-v/src/k210/hardware/k210_memorymap.h b/arch/risc-v/src/k210/hardware/k210_memorymap.h
index 16a6ec0..d9f5deb 100644
--- a/arch/risc-v/src/k210/hardware/k210_memorymap.h
+++ b/arch/risc-v/src/k210/hardware/k210_memorymap.h
@@ -42,7 +42,11 @@
#define K210_CLINT_BASE 0x02000000
#define K210_PLIC_BASE 0x0c000000
+#ifdef CONFIG_K210_WITH_QEMU
+#define K210_UART0_BASE 0x10010000
+#else
#define K210_UART0_BASE 0x38000000
+#endif
#define K210_GPIO_BASE 0x38001000
#endif /* __ARCH_RISCV_SRC_K210_HARDWARE_K210_MEMORYMAP_H */
diff --git a/arch/risc-v/src/rv64gc/up_initialstate.c b/arch/risc-v/src/k210/k210_cpuidlestack.c
similarity index 50%
copy from arch/risc-v/src/rv64gc/up_initialstate.c
copy to arch/risc-v/src/k210/k210_cpuidlestack.c
index 039631f..e070650 100644
--- a/arch/risc-v/src/rv64gc/up_initialstate.c
+++ b/arch/risc-v/src/k210/k210_cpuidlestack.c
@@ -1,8 +1,8 @@
/****************************************************************************
- * arch/risc-v/src/rv64gc/up_initialstate.c
+ * arch/risc-v/src/k210/k210_cpuidlestack.c
*
- * Copyright (C) 2011 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gn...@nuttx.org>
+ * Copyright (C) 2020 Masayuki Ishikawa. All rights reserved.
+ * Author: Masayuki Ishikawa <ma...@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -40,82 +40,67 @@
#include <nuttx/config.h>
#include <sys/types.h>
-#include <stdint.h>
-#include <string.h>
#include <nuttx/arch.h>
-#include <arch/irq.h>
+#include <nuttx/sched.h>
#include "up_internal.h"
-#include "up_arch.h"
+
+#ifdef CONFIG_SMP
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
- * Name: up_initial_state
+ * Name: up_cpu_idlestack
*
* Description:
- * A new thread is being started and a new TCB
- * has been created. This function is called to initialize
- * the processor specific portions of the new TCB.
+ * 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);
*
- * This function must setup the initial architecture registers
- * and/or stack so that execution will begin at tcb->start
- * on the next context switch.
+ * 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
+ * - adj_stack_ptr: Adjusted stack_alloc_ptr for HW. The initial value of
+ * the stack pointer.
+ *
+ * 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_SMP_STACK_SIZE.
*
****************************************************************************/
-void up_initial_state(struct tcb_s *tcb)
+int up_cpu_idlestack(int cpu, FAR struct tcb_s *tcb, size_t stack_size)
{
- struct xcptcontext *xcp = &tcb->xcp;
- uint32_t regval;
-
- /* Initialize the initial exception register context structure */
-
- memset(xcp, 0, sizeof(struct xcptcontext));
-
- /* Save the initial stack pointer. Hmmm.. the stack is set to the very
- * beginning of the stack region. Some functions may want to store data on
- * the caller's stack and it might be good to reserve some space. However,
- * only the start function would do that and we have control over that one
- */
-
- xcp->regs[REG_SP] = (uintptr_t)tcb->adj_stack_ptr;
-
- /* Save the task entry point */
-
- xcp->regs[REG_EPC] = (uintptr_t)tcb->start;
-
- /* If this task is running PIC, then set the PIC base register to the
- * address of the allocated D-Space region.
- */
-
-#ifdef CONFIG_PIC
-# warning "Missing logic"
+#if CONFIG_SMP_NCPUS > 1
+ (void)up_create_stack(tcb, stack_size, TCB_FLAG_TTYPE_KERNEL);
#endif
-
- /* Set privileged- or unprivileged-mode, depending on how NuttX is
- * configured and what kind of thread is being started.
- *
- * If the kernel build is not selected, then all threads run in
- * privileged thread mode.
- */
-
-#ifdef CONFIG_BUILD_KERNEL
-# warning "Missing logic"
-#endif
-
- /* Set the initial value of the interrupt context register.
- *
- * Since various RISC-V platforms use different interrupt
- * methodologies, the value of the interrupt context is
- * part specific.
- *
- */
-
- regval = up_get_newintctx();
- xcp->regs[REG_INT_CTX] = regval;
+ return OK;
}
+#endif /* CONFIG_SMP */
diff --git a/arch/risc-v/src/common/up_interruptcontext.c b/arch/risc-v/src/k210/k210_cpuindex.c
similarity index 74%
copy from arch/risc-v/src/common/up_interruptcontext.c
copy to arch/risc-v/src/k210/k210_cpuindex.c
index 8bdc595..055aa76 100644
--- a/arch/risc-v/src/common/up_interruptcontext.c
+++ b/arch/risc-v/src/k210/k210_cpuindex.c
@@ -1,8 +1,8 @@
/****************************************************************************
- * arch/risc-v/src/common/up_interruptcontext.c
+ * arch/risc-v/src/k210/k210_cpuindex.c
*
- * Copyright (C) 2011 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gn...@nuttx.org>
+ * Copyright (C) 2020 Masayuki Ishikawa. All rights reserved.
+ * Author: Masayuki Ishikawa <ma...@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -38,33 +38,39 @@
****************************************************************************/
#include <nuttx/config.h>
-
-#include <stdbool.h>
+#include <stdint.h>
#include <nuttx/arch.h>
-#include <nuttx/irq.h>
-
-#include "up_internal.h"
-/****************************************************************************
- * Private Types
- ****************************************************************************/
+#include "up_arch.h"
-/****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
+#ifdef CONFIG_SMP
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
- * Name: up_interrupt_context
+ * 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.
+ *
+ * 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.
*
- * Description: Return true is we are currently executing in
- * the interrupt handler context.
****************************************************************************/
-bool up_interrupt_context(void)
+int up_cpu_index(void)
{
- return g_current_regs != NULL;
+ int mhartid;
+
+ asm volatile ("csrr %0, mhartid": "=r" (mhartid));
+ return mhartid;
}
+
+#endif /* CONFIG_SMP */
diff --git a/arch/risc-v/src/k210/k210_cpupause.c b/arch/risc-v/src/k210/k210_cpupause.c
new file mode 100644
index 0000000..144cfd7
--- /dev/null
+++ b/arch/risc-v/src/k210/k210_cpupause.c
@@ -0,0 +1,332 @@
+/****************************************************************************
+ * arch/risc-v/src/k210/k210_cpupause.c
+ *
+ * Copyright (C) 2020 Masayuki Ishikawa. All rights reserved.
+ * Author: Masayuki Ishikawa <ma...@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <assert.h>
+#include <debug.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/sched_note.h>
+
+#include "up_arch.h"
+#include "sched/sched.h"
+#include "up_internal.h"
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#if 0
+#define DPRINTF(fmt, args...) llinfo(fmt, ##args)
+#else
+#define DPRINTF(fmt, args...) do {} while (0)
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/* These spinlocks are used in the SMP configuration in order to implement
+ * up_cpu_pause(). The protocol for CPUn to pause CPUm is as follows
+ *
+ * 1. The up_cpu_pause() implementation on CPUn locks both g_cpu_wait[m]
+ * and g_cpu_paused[m]. CPUn then waits spinning on g_cpu_paused[m].
+ * 2. CPUm receives the interrupt it (1) unlocks g_cpu_paused[m] and
+ * (2) locks g_cpu_wait[m]. The first unblocks CPUn and the second
+ * blocks CPUm in the interrupt handler.
+ *
+ * When CPUm resumes, CPUn unlocks g_cpu_wait[m] and the interrupt handler
+ * on CPUm continues. CPUm must, of course, also then unlock g_cpu_wait[m]
+ * so that it will be ready for the next pause operation.
+ */
+
+volatile spinlock_t g_cpu_wait[CONFIG_SMP_NCPUS];
+volatile spinlock_t g_cpu_paused[CONFIG_SMP_NCPUS];
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_cpu_pausereq
+ *
+ * Description:
+ * Return true if a pause request is pending for this CPU.
+ *
+ * Input Parameters:
+ * cpu - The index of the CPU to be queried
+ *
+ * Returned Value:
+ * true = a pause request is pending.
+ * false = no pasue request is pending.
+ *
+ ****************************************************************************/
+
+bool up_cpu_pausereq(int cpu)
+{
+ return spin_islocked(&g_cpu_paused[cpu]);
+}
+
+/****************************************************************************
+ * Name: up_cpu_paused
+ *
+ * Description:
+ * Handle a pause request from another CPU. Normally, this logic is
+ * executed from interrupt handling logic within the architecture-specific
+ * However, it is sometimes necessary necessary to perform the pending
+ * pause operation in other contexts where the interrupt cannot be taken
+ * in order to avoid deadlocks.
+ *
+ * This function performs the following operations:
+ *
+ * 1. It saves the current task state at the head of the current assigned
+ * task list.
+ * 2. It waits on a spinlock, then
+ * 3. Returns from interrupt, restoring the state of the new task at the
+ * head of the ready to run list.
+ *
+ * Input Parameters:
+ * cpu - The index of the CPU to be paused
+ *
+ * Returned Value:
+ * On success, OK is returned. Otherwise, a negated errno value indicating
+ * the nature of the failure is returned.
+ *
+ ****************************************************************************/
+
+int up_cpu_paused(int cpu)
+{
+ FAR struct tcb_s *tcb = this_task();
+
+ /* Update scheduler parameters */
+
+ sched_suspend_scheduler(tcb);
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION
+ /* Notify that we are paused */
+
+ sched_note_cpu_paused(tcb);
+#endif
+
+ /* Save the current context at CURRENT_REGS into the TCB at the head
+ * of the assigned task list for this CPU.
+ */
+
+ up_savestate(tcb->xcp.regs);
+
+ /* Wait for the spinlock to be released */
+
+ spin_unlock(&g_cpu_paused[cpu]);
+ spin_lock(&g_cpu_wait[cpu]);
+
+ /* Restore the exception context of the tcb at the (new) head of the
+ * assigned task list.
+ */
+
+ tcb = this_task();
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION
+ /* Notify that we have resumed */
+
+ sched_note_cpu_resumed(tcb);
+#endif
+
+ /* Reset scheduler parameters */
+
+ sched_resume_scheduler(tcb);
+
+ /* Then switch contexts. Any necessary address environment changes
+ * will be made when the interrupt returns.
+ */
+
+ up_restorestate(tcb->xcp.regs);
+
+ spin_unlock(&g_cpu_wait[cpu]);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: riscv_pause_handler
+ *
+ * Description:
+ * Inter-CPU interrupt handler
+ *
+ * Input Parameters:
+ * Standard interrupt handler inputs
+ *
+ * Returned Value:
+ * Should always return OK
+ *
+ ****************************************************************************/
+
+int riscv_pause_handler(int irq, void *c, FAR void *arg)
+{
+ int cpu = up_cpu_index();
+
+ /* Clear machine software interrupt */
+
+ putreg32(0, (uintptr_t)K210_CLINT_MSIP + (4 * cpu));
+
+ /* Check for false alarms. Such false could occur as a consequence of
+ * some deadlock breaking logic that might have already serviced the SG2
+ * interrupt by calling up_cpu_paused.
+ */
+
+ if (spin_islocked(&g_cpu_paused[cpu]))
+ {
+ return up_cpu_paused(cpu);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: up_cpu_pause
+ *
+ * Description:
+ * Save the state of the current task at the head of the
+ * g_assignedtasks[cpu] task list and then pause task execution on the
+ * CPU.
+ *
+ * This function is called by the OS when the logic executing on one CPU
+ * needs to modify the state of the g_assignedtasks[cpu] list for another
+ * CPU.
+ *
+ * Input Parameters:
+ * cpu - The index of the CPU to be stopped/
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int up_cpu_pause(int cpu)
+{
+ DPRINTF("cpu=%d\n", cpu);
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION
+ /* Notify of the pause event */
+
+ sched_note_cpu_pause(this_task(), cpu);
+#endif
+
+ DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu());
+
+ /* Take the both spinlocks. The g_cpu_wait spinlock will prevent the SGI2
+ * handler from returning until up_cpu_resume() is called; g_cpu_paused
+ * is a handshake that will prefent this function from returning until
+ * the CPU is actually paused.
+ */
+
+ DEBUGASSERT(!spin_islocked(&g_cpu_wait[cpu]) &&
+ !spin_islocked(&g_cpu_paused[cpu]));
+
+ spin_lock(&g_cpu_wait[cpu]);
+ spin_lock(&g_cpu_paused[cpu]);
+
+ /* Execute Pause IRQ to CPU(cpu) */
+
+ putreg32(1, (uintptr_t)K210_CLINT_MSIP + (4 * cpu));
+
+ /* Wait for the other CPU to unlock g_cpu_paused meaning that
+ * it is fully paused and ready for up_cpu_resume();
+ */
+
+ spin_lock(&g_cpu_paused[cpu]);
+
+ spin_unlock(&g_cpu_paused[cpu]);
+
+ /* On successful return g_cpu_wait will be locked, the other CPU will be
+ * spinninf on g_cpu_wait and will not continue until g_cpu_resume() is
+ * called. g_cpu_paused will be unlocked in any case.
+ */
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: up_cpu_resume
+ *
+ * Description:
+ * Restart the cpu after it was paused via up_cpu_pause(), restoring the
+ * state of the task at the head of the g_assignedtasks[cpu] list, and
+ * resume normal tasking.
+ *
+ * This function is called after up_cpu_pause in order resume operation of
+ * the CPU after modifying its g_assignedtasks[cpu] list.
+ *
+ * Input Parameters:
+ * cpu - The index of the CPU being re-started.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int up_cpu_resume(int cpu)
+{
+ DPRINTF("cpu=%d\n", cpu);
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION
+ /* Notify of the resume event */
+
+ sched_note_cpu_resume(this_task(), cpu);
+#endif
+
+ DEBUGASSERT(cpu >= 0 && cpu < CONFIG_SMP_NCPUS && cpu != this_cpu());
+
+ /* Release the spinlock. Releasing the spinlock will cause the SGI2
+ * handler on 'cpu' to continue and return from interrupt to the newly
+ * established thread.
+ */
+
+ DEBUGASSERT(spin_islocked(&g_cpu_wait[cpu]) &&
+ !spin_islocked(&g_cpu_paused[cpu]));
+
+ spin_unlock(&g_cpu_wait[cpu]);
+
+ return 0;
+}
diff --git a/arch/risc-v/src/k210/k210_timerisr.c b/arch/risc-v/src/k210/k210_cpustart.c
similarity index 50%
copy from arch/risc-v/src/k210/k210_timerisr.c
copy to arch/risc-v/src/k210/k210_cpustart.c
index 80c3652..bb27ef5 100644
--- a/arch/risc-v/src/k210/k210_timerisr.c
+++ b/arch/risc-v/src/k210/k210_cpustart.c
@@ -1,7 +1,7 @@
/****************************************************************************
- * arch/risc-v/src/k210/k210_timerisr.c
+ * arch/risc-v/src/k210/k210_cpustart.c
*
- * Copyright (C) 2019 Masayuki Ishikawa. All rights reserved.
+ * Copyright (C) 2020 Masayuki Ishikawa. All rights reserved.
* Author: Masayuki Ishikawa <ma...@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
@@ -40,106 +40,154 @@
#include <nuttx/config.h>
#include <stdint.h>
-#include <time.h>
+#include <assert.h>
#include <debug.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
#include <nuttx/arch.h>
-#include <arch/board/board.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/sched_note.h>
#include "up_arch.h"
+#include "sched/sched.h"
+#include "init/init.h"
+#include "up_internal.h"
+#include "chip.h"
-#include "k210.h"
-#include "k210_clockconfig.h"
+#ifdef CONFIG_SMP
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
-#define getreg64(a) (*(volatile uint64_t *)(a))
-#define putreg64(v,a) (*(volatile uint64_t *)(a) = (v))
+#if 0
+# define DPRINTF(fmt, args...) _err(fmt, ##args)
+#else
+# define DPRINTF(fmt, args...) do {} while (0)
+#endif
-#define TICK_COUNT ((k210_get_cpuclk() / 50) / TICK_PER_SEC)
+#ifdef CONFIG_DEBUG_FEATURES
+# define showprogress(c) up_lowputc(c)
+#else
+# define showprogress(c)
+#endif
/****************************************************************************
- * Private Data
+ * Public Data
****************************************************************************/
-static bool _b_tick_started = false;
+extern volatile bool g_serial_ok;
+extern int riscv_pause_handler(int irq, void *c, FAR void *arg);
/****************************************************************************
- * Private Functions
+ * Public Functions
****************************************************************************/
/****************************************************************************
- * Name: k210_reload_mtimecmp
+ * Name: k210_cpu_boot
+ *
+ * Description:
+ * Boot handler for cpu1
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
****************************************************************************/
-static void k210_reload_mtimecmp(void)
+void k210_cpu_boot(int cpu)
{
- irqstate_t flags = spin_lock_irqsave();
-
- uint64_t current;
- uint64_t next;
-
- if (!_b_tick_started)
+ if (1 < cpu)
{
- _b_tick_started = true;
- current = getreg64(K210_CLINT_MTIME);
+ return;
}
- else
+
+ /* Wait for g_serial_ok set by cpu0 when booting */
+
+ while (!g_serial_ok)
{
- current = getreg64(K210_CLINT_MTIMECMP);
}
- uint64_t tick = TICK_COUNT;
- next = current + tick;
+ /* Clear machine software interrupt for CPU(cpu) */
- putreg64(next, K210_CLINT_MTIMECMP);
+ putreg32(0, (uintptr_t)K210_CLINT_MSIP + (4 * cpu));
- spin_unlock_irqrestore(flags);
-}
+ /* Enable machine software interrupt for IPI to boot */
-/****************************************************************************
- * Name: k210_timerisr
- ****************************************************************************/
+ up_enable_irq(K210_IRQ_MSOFT);
-static int k210_timerisr(int irq, void *context, FAR void *arg)
-{
- k210_reload_mtimecmp();
+ /* Wait interrupt */
- /* Process timer interrupt */
+ asm("WFI");
- nxsched_process_timer();
- return 0;
-}
+ showprogress('b');
+ DPRINTF("CPU%d Started\n", this_cpu());
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
+ /* TODO: Setup FPU */
+
+ /* Clear machine software interrupt for CPU(cpu) */
+
+ putreg32(0, (uintptr_t)K210_CLINT_MSIP + (4 * cpu));
+
+#ifdef CONFIG_SCHED_INSTRUMENTATION
+ /* Notify that this CPU has started */
+
+ sched_note_cpu_started(this_task());
+#endif
+
+ (void)up_irq_enable();
+
+ /* Then transfer control to the IDLE task */
+
+ (void)nx_idle_task(0, NULL);
+}
/****************************************************************************
- * Name: riscv_timer_initialize
+ * Name: up_cpu_start
*
* Description:
- * This function is called during start-up to initialize
- * the timer interrupt.
+ * In an SMP configution, 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 is 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. Not stack has been alloced 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 from one to (CONFIG_SMP_NCPUS-1). (CPU
+ * 0 is already active)
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
*
****************************************************************************/
-void riscv_timer_initialize(void)
+int up_cpu_start(int cpu)
{
-#if 1
- /* Attach timer interrupt handler */
+ DPRINTF("cpu=%d\n", cpu);
- (void)irq_attach(K210_IRQ_MTIMER, k210_timerisr, NULL);
+#ifdef CONFIG_SCHED_INSTRUMENTATION
+ /* Notify of the start event */
- /* Reload CLINT mtimecmp */
+ sched_note_cpu_start(this_task(), cpu);
+#endif
- k210_reload_mtimecmp();
+ /* Send IPI to CPU(cpu) */
- /* And enable the timer interrupt */
+ putreg32(1, (uintptr_t)K210_CLINT_MSIP + (cpu * 4));
- up_enable_irq(K210_IRQ_MTIMER);
-#endif
+ return 0;
}
+#endif /* CONFIG_SMP */
diff --git a/arch/risc-v/src/k210/k210_head.S b/arch/risc-v/src/k210/k210_head.S
index 291d549..46ee266 100644
--- a/arch/risc-v/src/k210/k210_head.S
+++ b/arch/risc-v/src/k210/k210_head.S
@@ -54,9 +54,18 @@
__start:
+ /* Load mhartid (cpuid) */
+
+ csrr a0, mhartid
+
/* Set stack pointer to the idle thread stack */
- la sp, K210_IDLESTACK_TOP
+ bnez a0, 1f
+ la sp, K210_IDLESTACK0_TOP
+ j 2f
+1:
+ la sp, K210_IDLESTACK1_TOP
+2:
/* Disable all interrupts (i.e. timer, external) in mie */
@@ -67,9 +76,9 @@ __start:
la t0, __trap_vec
csrw mtvec, t0
- /* Jump to __k210_start */
+ /* Jump to __k210_start with mhartid */
- jal x1, __k210_start
+ j __k210_start
/* We shouldn't return from __k210_start */
@@ -89,6 +98,16 @@ _fini:
exception_common:
+#if 0
+ csrr gp, mcause /* exception cause */
+ addi tp, zero, 10 /* 10 = machine ecall */
+ bgtu gp, tp, normal_irq
+ ld sp, g_fstack_top /* Set sp to fault stack */
+
+normal_irq:
+ addi gp, zero, 0 /* clear */
+#endif
+
addi sp, sp, -XCPTCONTEXT_SIZE
sd x1, 1*8(sp) /* ra */
@@ -137,9 +156,20 @@ exception_common:
mv a1, sp /* context = sp */
#if CONFIG_ARCH_INTERRUPTSTACK > 3
+ /* Load mhartid (cpuid) */
+
+ csrr s0, mhartid
+
/* Switch to interrupt stack */
+ bnez s0, 3f
la sp, g_intstackbase
+ j 4f
+3:
+ la sp, g_intstackbase
+ addi sp, sp, -((CONFIG_ARCH_INTERRUPTSTACK) & ~7)
+4:
+
#endif
/* Call interrupt handler in C */
@@ -205,9 +235,9 @@ exception_common:
.type g_intstackalloc, object
.type g_intstackbase, object
g_intstackalloc:
- .skip ((CONFIG_ARCH_INTERRUPTSTACK & ~7))
+ .skip (((CONFIG_ARCH_INTERRUPTSTACK * 2) & ~7))
g_intstackbase:
.skip 8
.size g_intstackbase, 8
- .size g_intstackalloc, (CONFIG_ARCH_INTERRUPTSTACK & ~7)
+ .size g_intstackalloc, ((CONFIG_ARCH_INTERRUPTSTACK * 2) & ~7)
#endif
diff --git a/arch/risc-v/src/k210/k210_irq.c b/arch/risc-v/src/k210/k210_irq.c
index 7edeec5..4773f40 100644
--- a/arch/risc-v/src/k210/k210_irq.c
+++ b/arch/risc-v/src/k210/k210_irq.c
@@ -52,6 +52,24 @@
#include "k210.h"
/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef CONFIG_SMP
+/* For the case of configurations with multiple CPUs, then there must be one
+ * such value for each processor that can receive an interrupt.
+ */
+
+volatile uint64_t *g_current_regs[CONFIG_SMP_NCPUS];
+#else
+volatile uint64_t *g_current_regs[1];
+#endif
+
+#ifdef CONFIG_SMP
+extern int riscv_pause_handler(int irq, void *c, FAR void *arg);
+#endif
+
+/****************************************************************************
* Public Functions
****************************************************************************/
@@ -98,12 +116,23 @@ void up_irqinitialize(void)
/* currents_regs is non-NULL only while processing an interrupt */
- g_current_regs = NULL;
+ CURRENT_REGS = NULL;
/* Attach the ecall interrupt handler */
irq_attach(K210_IRQ_ECALLM, up_swint, NULL);
+#ifdef CONFIG_SMP
+ /* Clear MSOFT for CPU0 */
+
+ putreg32(0, K210_CLINT_MSIP);
+
+ /* Setup MSOFT for CPU0 with pause handler */
+
+ irq_attach(K210_IRQ_MSOFT, riscv_pause_handler, NULL);
+ up_enable_irq(K210_IRQ_MSOFT);
+#endif
+
#ifndef CONFIG_SUPPRESS_INTERRUPTS
/* And finally, enable interrupts */
@@ -123,9 +152,15 @@ void up_irqinitialize(void)
void up_disable_irq(int irq)
{
int extirq;
- uint32_t oldstat;
+ uint64_t oldstat;
- if (irq == K210_IRQ_MTIMER)
+ if (irq == K210_IRQ_MSOFT)
+ {
+ /* Read mstatus & clear machine software interrupt enable in mie */
+
+ asm volatile ("csrrc %0, mie, %1": "=r" (oldstat) : "r"(MIE_MSIE));
+ }
+ else if (irq == K210_IRQ_MTIMER)
{
/* Read mstatus & clear machine timer interrupt enable in mie */
@@ -160,9 +195,15 @@ void up_disable_irq(int irq)
void up_enable_irq(int irq)
{
int extirq;
- uint32_t oldstat;
+ uint64_t oldstat;
+
+ if (irq == K210_IRQ_MSOFT)
+ {
+ /* Read mstatus & set machine software interrupt enable in mie */
- if (irq == K210_IRQ_MTIMER)
+ asm volatile ("csrrs %0, mie, %1": "=r" (oldstat) : "r"(MIE_MSIE));
+ }
+ else if (irq == K210_IRQ_MTIMER)
{
/* Read mstatus & set machine timer interrupt enable in mie */
@@ -225,7 +266,7 @@ void up_ack_irq(int irq)
irqstate_t up_irq_save(void)
{
- uint32_t oldstat;
+ uint64_t oldstat;
/* Read mstatus & clear machine interrupt enable (MIE) in mstatus */
@@ -258,7 +299,7 @@ void up_irq_restore(irqstate_t flags)
irqstate_t up_irq_enable(void)
{
- uint32_t oldstat;
+ uint64_t oldstat;
#if 1
/* Enable MEIE (machine external interrupt enable) */
diff --git a/arch/risc-v/src/k210/k210_irq_dispatch.c b/arch/risc-v/src/k210/k210_irq_dispatch.c
index 51ee235..d858ff3 100644
--- a/arch/risc-v/src/k210/k210_irq_dispatch.c
+++ b/arch/risc-v/src/k210/k210_irq_dispatch.c
@@ -53,7 +53,7 @@
* Public Data
****************************************************************************/
-volatile uint64_t * g_current_regs;
+extern void up_fault(int irq, uint64_t *regs);
/****************************************************************************
* Public Functions
@@ -68,6 +68,13 @@ void *k210_dispatch_irq(uint64_t vector, uint64_t *regs)
uint32_t irq = (vector >> (27 + 32)) | (vector & 0xf);
uint64_t *mepc = regs;
+ /* Check if fault happened */
+
+ if (vector < 11)
+ {
+ up_fault((int)irq, regs);
+ }
+
/* Firstly, check if the irq is machine external interrupt */
if (K210_IRQ_MEXT == irq)
@@ -94,17 +101,22 @@ void *k210_dispatch_irq(uint64_t vector, uint64_t *regs)
PANIC();
#else
/* Current regs non-zero indicates that we are processing an interrupt;
- * g_current_regs is also used to manage interrupt level context switches.
+ * CURRENT_REGS is also used to manage interrupt level context switches.
*
* Nested interrupts are not supported
*/
- DEBUGASSERT(g_current_regs == NULL);
- g_current_regs = regs;
+ ASSERT(CURRENT_REGS == NULL);
+ CURRENT_REGS = regs;
- /* Deliver the IRQ */
+ /* MEXT means no interrupt */
- irq_dispatch(irq, regs);
+ if (K210_IRQ_MEXT != irq)
+ {
+ /* Deliver the IRQ */
+
+ irq_dispatch(irq, regs);
+ }
if (K210_IRQ_MEXT <= irq)
{
@@ -115,13 +127,13 @@ void *k210_dispatch_irq(uint64_t vector, uint64_t *regs)
#endif
/* If a context switch occurred while processing the interrupt then
- * g_current_regs may have change value. If we return any value different
+ * CURRENT_REGS may have change value. If we return any value different
* from the input regs, then the lower level will know that a context
* switch occurred during interrupt processing.
*/
- regs = (uint64_t *)g_current_regs;
- g_current_regs = NULL;
+ regs = (uint64_t *)CURRENT_REGS;
+ CURRENT_REGS = NULL;
return regs;
}
diff --git a/arch/risc-v/src/k210/k210_memorymap.h b/arch/risc-v/src/k210/k210_memorymap.h
index 2c79271..bf9de37 100644
--- a/arch/risc-v/src/k210/k210_memorymap.h
+++ b/arch/risc-v/src/k210/k210_memorymap.h
@@ -55,7 +55,11 @@
#endif
#define K210_IDLESTACK_SIZE (CONFIG_IDLETHREAD_STACKSIZE & ~7)
-#define K210_IDLESTACK_TOP (K210_IDLESTACK_BASE + K210_IDLESTACK_SIZE)
+
+#define K210_IDLESTACK0_TOP (K210_IDLESTACK_BASE + K210_IDLESTACK_SIZE)
+#define K210_IDLESTACK1_TOP (K210_IDLESTACK0_TOP + K210_IDLESTACK_SIZE)
+
+#define K210_IDLESTACK_TOP (K210_IDLESTACK1_TOP)
#endif /* _ARCH_RISCV_SRC_K210_K210_MEMORYMAP_H */
diff --git a/arch/risc-v/src/k210/k210_start.c b/arch/risc-v/src/k210/k210_start.c
index 8727231..f1289f1 100644
--- a/arch/risc-v/src/k210/k210_start.c
+++ b/arch/risc-v/src/k210/k210_start.c
@@ -36,8 +36,10 @@
#include <nuttx/config.h>
+#include <nuttx/arch.h>
#include <arch/board/board.h>
+#include "up_arch.h"
#include "k210_clockconfig.h"
#include "k210.h"
#include "chip.h"
@@ -67,6 +69,9 @@
*/
uintptr_t g_idle_topstack = K210_IDLESTACK_TOP;
+volatile bool g_serial_ok = false;
+
+extern void k210_cpu_boot(uint32_t);
/****************************************************************************
* Public Functions
@@ -76,11 +81,18 @@ uintptr_t g_idle_topstack = K210_IDLESTACK_TOP;
* Name: k210_start
****************************************************************************/
-void __k210_start(void)
+void __k210_start(uint32_t mhartid)
{
const uint32_t *src;
uint32_t *dest;
+ g_serial_ok = false;
+
+ if (0 < mhartid)
+ {
+ goto cpu1;
+ }
+
/* Clear .bss. We'll do this inline (vs. calling memset) just to be
* certain that there are no issues with the state of global variables.
*/
@@ -117,6 +129,8 @@ void __k210_start(void)
showprogress('B');
+ g_serial_ok = true;
+
/* Do board initialization */
k210_boardinitialize();
@@ -127,7 +141,16 @@ void __k210_start(void)
nx_start();
- /* Shouldn't get here */
+cpu1:
- for (; ; );
+ showprogress('a');
+
+#if defined(CONFIG_SMP) && (CONFIG_SMP_NCPUS == 2)
+ k210_cpu_boot(mhartid);
+#endif
+
+ while (true)
+ {
+ asm("WFI");
+ }
}
diff --git a/arch/risc-v/src/k210/k210_timerisr.c b/arch/risc-v/src/k210/k210_timerisr.c
index 80c3652..0c0b60d 100644
--- a/arch/risc-v/src/k210/k210_timerisr.c
+++ b/arch/risc-v/src/k210/k210_timerisr.c
@@ -58,7 +58,11 @@
#define getreg64(a) (*(volatile uint64_t *)(a))
#define putreg64(v,a) (*(volatile uint64_t *)(a) = (v))
+#ifdef CONFIG_K210_WITH_QEMU
+#define TICK_COUNT (10000000 / TICK_PER_SEC)
+#else
#define TICK_COUNT ((k210_get_cpuclk() / 50) / TICK_PER_SEC)
+#endif
/****************************************************************************
* Private Data
diff --git a/arch/risc-v/src/k210/up_schedulesigaction.c b/arch/risc-v/src/k210/up_schedulesigaction.c
index 2a3c2f4..24f3896 100644
--- a/arch/risc-v/src/k210/up_schedulesigaction.c
+++ b/arch/risc-v/src/k210/up_schedulesigaction.c
@@ -53,6 +53,8 @@
#include "up_internal.h"
#include "up_arch.h"
+#include "irq/irq.h"
+
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -90,10 +92,11 @@
*
****************************************************************************/
+#ifndef CONFIG_SMP
void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
{
irqstate_t flags;
- uint32_t int_ctx;
+ uint64_t int_ctx;
sinfo("tcb=0x%p sigdeliver=0x%p\n", tcb, sigdeliver);
@@ -109,8 +112,8 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
* being delivered to the currently executing task.
*/
- sinfo("rtcb=0x%p g_current_regs=0x%p\n",
- this_task(), g_current_regs);
+ sinfo("rtcb=0x%p CURRENT_REGS=0x%p\n",
+ this_task(), CURRENT_REGS);
if (tcb == this_task())
{
@@ -118,7 +121,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
* a task is signalling itself for some reason.
*/
- if (!g_current_regs)
+ if (!CURRENT_REGS)
{
/* In this case just deliver the signal now. */
@@ -134,7 +137,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
* logic would fail in the strange case where we are in an
* interrupt handler, the thread is signalling itself, but
* a context switch to another task has occurred so that
- * g_current_regs does not refer to the thread of this_task()!
+ * CURRENT_REGS does not refer to the thread of this_task()!
*/
else
@@ -145,19 +148,19 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
*/
tcb->xcp.sigdeliver = sigdeliver;
- tcb->xcp.saved_epc = g_current_regs[REG_EPC];
- tcb->xcp.saved_int_ctx = g_current_regs[REG_INT_CTX];
+ tcb->xcp.saved_epc = CURRENT_REGS[REG_EPC];
+ tcb->xcp.saved_int_ctx = CURRENT_REGS[REG_INT_CTX];
/* Then set up to vector to the trampoline with interrupts
* disabled
*/
- g_current_regs[REG_EPC] = (uintptr_t)up_sigdeliver;
+ CURRENT_REGS[REG_EPC] = (uintptr_t)up_sigdeliver;
- int_ctx = g_current_regs[REG_INT_CTX];
+ int_ctx = CURRENT_REGS[REG_INT_CTX];
int_ctx &= ~MSTATUS_MIE;
- g_current_regs[REG_INT_CTX] = int_ctx;
+ CURRENT_REGS[REG_INT_CTX] = int_ctx;
/* And make sure that the saved context in the TCB
* is the same as the interrupt return context.
@@ -165,9 +168,9 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
up_savestate(tcb->xcp.regs);
- sinfo("PC/STATUS Saved: %08x/%08x New: %08x/%08x\n",
+ sinfo("PC/STATUS Saved: %016x/%016x New: %016x/%016x\n",
tcb->xcp.saved_epc, tcb->xcp.saved_status,
- g_current_regs[REG_EPC], g_current_regs[REG_INT_CTX]);
+ CURRENT_REGS[REG_EPC], CURRENT_REGS[REG_INT_CTX]);
}
}
@@ -199,7 +202,7 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
tcb->xcp.regs[REG_INT_CTX] = int_ctx;
- sinfo("PC/STATUS Saved: %08x/%08x New: %08x/%08x\n",
+ sinfo("PC/STATUS Saved: %016x/%016x New: %016x/%016x\n",
tcb->xcp.saved_epc, tcb->xcp.saved_status,
tcb->xcp.regs[REG_EPC], tcb->xcp.regs[REG_INT_CTX]);
}
@@ -207,3 +210,192 @@ void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
leave_critical_section(flags);
}
+#endif /* !CONFIG_SMP */
+
+#ifdef CONFIG_SMP
+void up_schedule_sigaction(struct tcb_s *tcb, sig_deliver_t sigdeliver)
+{
+ irqstate_t flags;
+ uint64_t int_ctx;
+ int cpu;
+ int me;
+
+ sinfo("tcb=0x%p sigdeliver=0x%p\n", tcb, sigdeliver);
+
+ /* Make sure that interrupts are disabled */
+
+ flags = enter_critical_section();
+
+ /* Refuse to handle nested signal actions */
+
+ if (!tcb->xcp.sigdeliver)
+ {
+ /* First, handle some special cases when the signal is being delivered
+ * to task that is currently executing on any CPU.
+ */
+
+ sinfo("rtcb=0x%p CURRENT_REGS=0x%p\n", this_task(), CURRENT_REGS);
+
+ if (tcb->task_state == TSTATE_TASK_RUNNING)
+ {
+ me = this_cpu();
+ cpu = tcb->cpu;
+
+ /* CASE 1: We are not in an interrupt handler and a task is
+ * signaling itself for some reason.
+ */
+
+ if (cpu == me && !CURRENT_REGS)
+ {
+ /* In this case just deliver the signal now.
+ * REVISIT: Signal handler will run in a critical section!
+ */
+
+ sigdeliver(tcb);
+ }
+
+ /* CASE 2: The task that needs to receive the signal is running.
+ * This could happen if the task is running on another CPU OR if
+ * we are in an interrupt handler and the task is running on this
+ * CPU. In the former case, we will have to PAUSE the other CPU
+ * first. But in either case, we will have to modify the return
+ * state as well as the state in the TCB.
+ */
+
+ else
+ {
+ /* If we signaling a task running on the other CPU, we have
+ * to PAUSE the other CPU.
+ */
+
+ if (cpu != me)
+ {
+ /* Pause the CPU */
+
+ up_cpu_pause(cpu);
+
+ /* Wait while the pause request is pending */
+
+ while (up_cpu_pausereq(cpu))
+ {
+ }
+
+ /* Now tcb on the other CPU can be accessed safely */
+
+ /* Copy tcb->xcp.regs to tcp.xcp.saved. These will be restored
+ * by the signal trampoline after the signal has been delivered.
+ */
+
+ tcb->xcp.sigdeliver = (FAR void *)sigdeliver;
+ tcb->xcp.saved_epc = tcb->xcp.regs[REG_EPC];
+ tcb->xcp.saved_int_ctx = tcb->xcp.regs[REG_INT_CTX];
+
+ /* Then set up vector to the trampoline with interrupts
+ * disabled. We must already be in privileged thread mode
+ * to be here.
+ */
+
+ tcb->xcp.regs[REG_EPC] = (uintptr_t)up_sigdeliver;
+
+ int_ctx = tcb->xcp.regs[REG_INT_CTX];
+ int_ctx &= ~MSTATUS_MIE;
+
+ tcb->xcp.regs[REG_INT_CTX] = int_ctx;
+ }
+ else
+ {
+ /* tcb is running on the same CPU */
+
+ /* Save the return EPC and STATUS registers. These will be
+ * restored by the signal trampoline after the signal has been
+ * delivered.
+ */
+
+ tcb->xcp.sigdeliver = (FAR void *)sigdeliver;
+ tcb->xcp.saved_epc = CURRENT_REGS[REG_EPC];
+ tcb->xcp.saved_int_ctx = CURRENT_REGS[REG_INT_CTX];
+
+ /* Then set up vector to the trampoline with interrupts
+ * disabled. The kernel-space trampoline must run in
+ * privileged thread mode.
+ */
+
+ CURRENT_REGS[REG_EPC] = (uintptr_t)up_sigdeliver;
+
+ int_ctx = CURRENT_REGS[REG_INT_CTX];
+ int_ctx &= ~MSTATUS_MIE;
+
+ CURRENT_REGS[REG_INT_CTX] = int_ctx;
+
+ /* And make sure that the saved context in the TCB is the same
+ * as the interrupt return context.
+ */
+
+ up_savestate(tcb->xcp.regs);
+ }
+
+ /* Increment the IRQ lock count so that when the task is restarted,
+ * it will hold the IRQ spinlock.
+ */
+
+ DEBUGASSERT(tcb->irqcount < INT16_MAX);
+ tcb->irqcount++;
+
+ /* In an SMP configuration, the interrupt disable logic also
+ * involves spinlocks that are configured per the TCB irqcount
+ * field. This is logically equivalent to enter_critical_section().
+ * The matching call to leave_critical_section() will be
+ * performed in up_sigdeliver().
+ */
+
+ spin_setbit(&g_cpu_irqset, cpu, &g_cpu_irqsetlock,
+ &g_cpu_irqlock);
+
+ /* RESUME the other CPU if it was PAUSED */
+
+ if (cpu != me)
+ {
+ up_cpu_resume(cpu);
+ }
+ }
+ }
+
+ /* Otherwise, we are (1) signaling a task is not running from an
+ * interrupt handler or (2) we are not in an interrupt handler and the
+ * running task is signaling some other non-running task.
+ */
+
+ else
+ {
+ /* Save the return EPC and STATUS registers. These will be
+ * by the signal trampoline after the signal has been delivered.
+ */
+
+ tcb->xcp.sigdeliver = (FAR void *)sigdeliver;
+ tcb->xcp.saved_epc = tcb->xcp.regs[REG_EPC];
+ tcb->xcp.saved_int_ctx = tcb->xcp.regs[REG_INT_CTX];
+
+ /* Increment the IRQ lock count so that when the task is restarted,
+ * it will hold the IRQ spinlock.
+ */
+
+ DEBUGASSERT(tcb->irqcount < INT16_MAX);
+ tcb->irqcount++;
+
+ /* Then set up to vector to the trampoline with interrupts
+ * disabled. We must already be in privileged thread mode to be
+ * here.
+ */
+
+ tcb->xcp.regs[REG_EPC] = (uintptr_t)up_sigdeliver;
+
+ int_ctx = tcb->xcp.regs[REG_INT_CTX];
+ int_ctx &= ~MSTATUS_MIE;
+
+ tcb->xcp.regs[REG_INT_CTX] = int_ctx;
+ }
+ }
+
+ leave_critical_section(flags);
+}
+#endif /* CONFIG_SMP */
diff --git a/arch/risc-v/src/rv64gc/up_assert.c b/arch/risc-v/src/rv64gc/up_assert.c
index e9c3871..06e781d 100644
--- a/arch/risc-v/src/rv64gc/up_assert.c
+++ b/arch/risc-v/src/rv64gc/up_assert.c
@@ -52,8 +52,10 @@
#include <arch/board/board.h>
-#include "up_arch.h"
#include "sched/sched.h"
+#include "irq/irq.h"
+
+#include "up_arch.h"
#include "up_internal.h"
/****************************************************************************
@@ -156,48 +158,48 @@ static inline void up_registerdump(void)
{
/* Are user registers available from interrupt processing? */
- if (g_current_regs)
+ if (CURRENT_REGS)
{
_alert("EPC:%016x \n",
- g_current_regs[REG_EPC]);
+ CURRENT_REGS[REG_EPC]);
_alert("A0:%016x A1:%016x A2:%016x A3:%016x \n",
- g_current_regs[REG_A0], g_current_regs[REG_A1],
- g_current_regs[REG_A2], g_current_regs[REG_A3]);
+ CURRENT_REGS[REG_A0], CURRENT_REGS[REG_A1],
+ CURRENT_REGS[REG_A2], CURRENT_REGS[REG_A3]);
_alert("A4:%016x A5:%016x A6:%016x A7:%016x \n",
- g_current_regs[REG_A4], g_current_regs[REG_A5],
- g_current_regs[REG_A6], g_current_regs[REG_A7]);
+ CURRENT_REGS[REG_A4], CURRENT_REGS[REG_A5],
+ CURRENT_REGS[REG_A6], CURRENT_REGS[REG_A7]);
_alert("T0:%016x T1:%016x T2:%016x T3:%016x \n",
- g_current_regs[REG_T0], g_current_regs[REG_T1],
- g_current_regs[REG_T2], g_current_regs[REG_T3]);
+ CURRENT_REGS[REG_T0], CURRENT_REGS[REG_T1],
+ CURRENT_REGS[REG_T2], CURRENT_REGS[REG_T3]);
_alert("T4:%016x T5:%016x T6:%016x \n",
- g_current_regs[REG_T4], g_current_regs[REG_T5],
- g_current_regs[REG_T6]);
+ CURRENT_REGS[REG_T4], CURRENT_REGS[REG_T5],
+ CURRENT_REGS[REG_T6]);
_alert("S0:%016x S1:%016x S2:%016x S3:%016x \n",
- g_current_regs[REG_S0], g_current_regs[REG_S1],
- g_current_regs[REG_S2], g_current_regs[REG_S3]);
+ CURRENT_REGS[REG_S0], CURRENT_REGS[REG_S1],
+ CURRENT_REGS[REG_S2], CURRENT_REGS[REG_S3]);
_alert("S4:%016x S5:%016x S6:%016x S7:%016x \n",
- g_current_regs[REG_S4], g_current_regs[REG_S5],
- g_current_regs[REG_S6], g_current_regs[REG_S7]);
+ CURRENT_REGS[REG_S4], CURRENT_REGS[REG_S5],
+ CURRENT_REGS[REG_S6], CURRENT_REGS[REG_S7]);
_alert("S8:%016x S9:%016x S10:%016x S11:%016x \n",
- g_current_regs[REG_S8], g_current_regs[REG_S9],
- g_current_regs[REG_S10], g_current_regs[REG_S11]);
+ CURRENT_REGS[REG_S8], CURRENT_REGS[REG_S9],
+ CURRENT_REGS[REG_S10], CURRENT_REGS[REG_S11]);
#ifdef RISCV_SAVE_GP
_alert("GP:%016x SP:%016x FP:%016x TP:%016x RA:%016x \n",
- g_current_regs[REG_GP], g_current_regs[REG_SP],
- g_current_regs[REG_FP], g_current_regs[REG_TP],
- g_current_regs[REG_RA]);
+ CURRENT_REGS[REG_GP], CURRENT_REGS[REG_SP],
+ CURRENT_REGS[REG_FP], CURRENT_REGS[REG_TP],
+ CURRENT_REGS[REG_RA]);
#else
_alert("SP:%016x FP:%016x TP:%016x RA:%016x \n",
- g_current_regs[REG_SP], g_current_regs[REG_FP],
- g_current_regs[REG_TP], g_current_regs[REG_RA]);
+ CURRENT_REGS[REG_SP], CURRENT_REGS[REG_FP],
+ CURRENT_REGS[REG_TP], CURRENT_REGS[REG_RA]);
#endif
}
}
@@ -259,10 +261,10 @@ static void up_dumpstate(void)
/* Extract the user stack pointer */
- sp = g_current_regs[REG_SP];
+ sp = CURRENT_REGS[REG_SP];
_alert("sp: %016x\n", sp);
}
- else if (g_current_regs)
+ else if (CURRENT_REGS)
{
_alert("ERROR: Stack pointer is not within the interrupt stack\n");
up_stackdump(istackbase - istacksize, istackbase);
@@ -309,11 +311,17 @@ static void _up_assert(int errorcode)
/* Are we in an interrupt handler or the idle task? */
- if (g_current_regs || running_task()->flink == NULL)
+ if (CURRENT_REGS || running_task()->flink == NULL)
{
(void)up_irq_save();
for (; ; )
{
+#ifdef CONFIG_SMP
+ /* Try (again) to stop activity on other CPUs */
+
+ (void)spin_trylock(&g_cpu_irqlock);
+#endif
+
#if CONFIG_BOARD_RESET_ON_ASSERT >= 1
board_reset(CONFIG_BOARD_ASSERT_RESET_VALUE);
#endif
@@ -379,6 +387,15 @@ void up_assert(const uint8_t *filename, int lineno)
(void)syslog_flush();
+#ifdef CONFIG_SMP
+#if CONFIG_TASK_NAME_SIZE > 0
+ _alert("Assertion failed CPU%d at file:%s line: %d task: %s\n",
+ up_cpu_index(), filename, lineno, rtcb->name);
+#else
+ _alert("Assertion failed CPU%d at file:%s line: %d\n",
+ up_cpu_index(), filename, lineno);
+#endif
+#else
#if CONFIG_TASK_NAME_SIZE > 0
_alert("Assertion failed at file:%s line: %d task: %s\n",
filename, lineno, rtcb->name);
@@ -386,9 +403,16 @@ void up_assert(const uint8_t *filename, int lineno)
_alert("Assertion failed at file:%s line: %d\n",
filename, lineno);
#endif
+#endif
up_dumpstate();
+#ifdef CONFIG_SMP
+ /* Show the CPU number */
+
+ _alert("CPU%d:\n", up_cpu_index());
+#endif
+
/* Dump the state of all tasks (if available) */
up_showtasks();
diff --git a/arch/risc-v/src/rv64gc/up_blocktask.c b/arch/risc-v/src/rv64gc/up_blocktask.c
index 2c0d7f7..d63a8e0 100644
--- a/arch/risc-v/src/rv64gc/up_blocktask.c
+++ b/arch/risc-v/src/rv64gc/up_blocktask.c
@@ -117,10 +117,10 @@ void up_block_task(struct tcb_s *tcb, tstate_t task_state)
/* Are we in an interrupt handler? */
- if (g_current_regs)
+ if (CURRENT_REGS)
{
/* Yes, then we have to do things differently.
- * Just copy the g_current_regs into the OLD rtcb.
+ * Just copy the CURRENT_REGS into the OLD rtcb.
*/
up_savestate(rtcb->xcp.regs);
diff --git a/arch/risc-v/src/rv64gc/up_initialstate.c b/arch/risc-v/src/rv64gc/up_fault.c
similarity index 52%
copy from arch/risc-v/src/rv64gc/up_initialstate.c
copy to arch/risc-v/src/rv64gc/up_fault.c
index 039631f..e04fa3e 100644
--- a/arch/risc-v/src/rv64gc/up_initialstate.c
+++ b/arch/risc-v/src/rv64gc/up_fault.c
@@ -1,8 +1,8 @@
/****************************************************************************
- * arch/risc-v/src/rv64gc/up_initialstate.c
+ * arch/risc-v/src/rv64gc/up_fault.c
*
- * Copyright (C) 2011 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gn...@nuttx.org>
+ * Copyright (C) 2020 Masayuki Ishikawa. All rights reserved.
+ * Author: Masayuki Ishikawa <ma...@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -39,83 +39,85 @@
#include <nuttx/config.h>
-#include <sys/types.h>
#include <stdint.h>
-#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <debug.h>
+#include <nuttx/irq.h>
#include <nuttx/arch.h>
-#include <arch/irq.h>
+#include <nuttx/board.h>
+#include <nuttx/syslog/syslog.h>
+
+#include <arch/board/board.h>
+
+#include "sched/sched.h"
+#include "irq/irq.h"
-#include "up_internal.h"
#include "up_arch.h"
+#include "up_internal.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
- * Name: up_initial_state
+ * Name: up_fault
*
* Description:
- * A new thread is being started and a new TCB
- * has been created. This function is called to initialize
- * the processor specific portions of the new TCB.
- *
- * This function must setup the initial architecture registers
- * and/or stack so that execution will begin at tcb->start
- * on the next context switch.
+ * This is Fault exception handler.
*
****************************************************************************/
-void up_initial_state(struct tcb_s *tcb)
+void up_fault(int irq, uint64_t *regs)
{
- struct xcptcontext *xcp = &tcb->xcp;
- uint32_t regval;
+ CURRENT_REGS = regs;
- /* Initialize the initial exception register context structure */
+ _alert("EPC:%016x\n",
+ CURRENT_REGS[REG_EPC]);
- memset(xcp, 0, sizeof(struct xcptcontext));
+ _alert("Fault IRQ=%d \n", irq);
- /* Save the initial stack pointer. Hmmm.. the stack is set to the very
- * beginning of the stack region. Some functions may want to store data on
- * the caller's stack and it might be good to reserve some space. However,
- * only the start function would do that and we have control over that one
- */
+ /* Dump register info */
- xcp->regs[REG_SP] = (uintptr_t)tcb->adj_stack_ptr;
+ _alert("A0:%016x A1:%016x A2:%016x A3:%016x \n",
+ CURRENT_REGS[REG_A0], CURRENT_REGS[REG_A1],
+ CURRENT_REGS[REG_A2], CURRENT_REGS[REG_A3]);
- /* Save the task entry point */
+ _alert("A4:%016x A5:%016x A6:%016x A7:%016x \n",
+ CURRENT_REGS[REG_A4], CURRENT_REGS[REG_A5],
+ CURRENT_REGS[REG_A6], CURRENT_REGS[REG_A7]);
- xcp->regs[REG_EPC] = (uintptr_t)tcb->start;
+ _alert("T0:%016x T1:%016x T2:%016x T3:%016x \n",
+ CURRENT_REGS[REG_T0], CURRENT_REGS[REG_T1],
+ CURRENT_REGS[REG_T2], CURRENT_REGS[REG_T3]);
- /* If this task is running PIC, then set the PIC base register to the
- * address of the allocated D-Space region.
- */
+ _alert("T4:%016x T5:%016x T6:%016x \n",
+ CURRENT_REGS[REG_T4], CURRENT_REGS[REG_T5],
+ CURRENT_REGS[REG_T6]);
-#ifdef CONFIG_PIC
-# warning "Missing logic"
-#endif
+ _alert("S0:%016x S1:%016x S2:%016x S3:%016x \n",
+ CURRENT_REGS[REG_S0], CURRENT_REGS[REG_S1],
+ CURRENT_REGS[REG_S2], CURRENT_REGS[REG_S3]);
- /* Set privileged- or unprivileged-mode, depending on how NuttX is
- * configured and what kind of thread is being started.
- *
- * If the kernel build is not selected, then all threads run in
- * privileged thread mode.
- */
+ _alert("S4:%016x S5:%016x S6:%016x S7:%016x \n",
+ CURRENT_REGS[REG_S4], CURRENT_REGS[REG_S5],
+ CURRENT_REGS[REG_S6], CURRENT_REGS[REG_S7]);
-#ifdef CONFIG_BUILD_KERNEL
-# warning "Missing logic"
-#endif
+ _alert("S8:%016x S9:%016x S10:%016x S11:%016x \n",
+ CURRENT_REGS[REG_S8], CURRENT_REGS[REG_S9],
+ CURRENT_REGS[REG_S10], CURRENT_REGS[REG_S11]);
- /* Set the initial value of the interrupt context register.
- *
- * Since various RISC-V platforms use different interrupt
- * methodologies, the value of the interrupt context is
- * part specific.
- *
- */
+#ifdef RISCV_SAVE_GP
+ _alert("GP:%016x SP:%016x FP:%016x TP:%016x RA:%016x \n",
+ CURRENT_REGS[REG_GP], CURRENT_REGS[REG_SP],
+ CURRENT_REGS[REG_FP], CURRENT_REGS[REG_TP],
+ CURRENT_REGS[REG_RA]);
+#else
+ _alert("SP:%016x FP:%016x TP:%016x RA:%016x \n",
+ CURRENT_REGS[REG_SP], CURRENT_REGS[REG_FP],
+ CURRENT_REGS[REG_TP], CURRENT_REGS[REG_RA]);
+#endif
- regval = up_get_newintctx();
- xcp->regs[REG_INT_CTX] = regval;
+ (void)up_irq_save();
}
-
diff --git a/arch/risc-v/src/rv64gc/up_initialstate.c b/arch/risc-v/src/rv64gc/up_initialstate.c
index 039631f..223a113 100644
--- a/arch/risc-v/src/rv64gc/up_initialstate.c
+++ b/arch/risc-v/src/rv64gc/up_initialstate.c
@@ -70,7 +70,7 @@
void up_initial_state(struct tcb_s *tcb)
{
struct xcptcontext *xcp = &tcb->xcp;
- uint32_t regval;
+ uint64_t regval;
/* Initialize the initial exception register context structure */
diff --git a/arch/risc-v/src/rv64gc/up_releasepending.c b/arch/risc-v/src/rv64gc/up_releasepending.c
index 367a1d5..1d6bf45 100644
--- a/arch/risc-v/src/rv64gc/up_releasepending.c
+++ b/arch/risc-v/src/rv64gc/up_releasepending.c
@@ -87,10 +87,10 @@ void up_release_pending(void)
/* Are we operating in interrupt context? */
- if (g_current_regs)
+ if (CURRENT_REGS)
{
/* Yes, then we have to do things differently.
- * Just copy the g_current_regs into the OLD rtcb.
+ * Just copy the CURRENT_REGS into the OLD rtcb.
*/
up_savestate(rtcb->xcp.regs);
diff --git a/arch/risc-v/src/rv64gc/up_reprioritizertr.c b/arch/risc-v/src/rv64gc/up_reprioritizertr.c
index 3d582ec..049a103 100644
--- a/arch/risc-v/src/rv64gc/up_reprioritizertr.c
+++ b/arch/risc-v/src/rv64gc/up_reprioritizertr.c
@@ -140,10 +140,10 @@ void up_reprioritize_rtr(struct tcb_s *tcb, uint8_t priority)
/* Are we in an interrupt handler? */
- if (g_current_regs)
+ if (CURRENT_REGS)
{
/* Yes, then we have to do things differently.
- * Just copy the g_current_regs into the OLD rtcb.
+ * Just copy the CURRENT_REGS into the OLD rtcb.
*/
up_savestate(rtcb->xcp.regs);
diff --git a/arch/risc-v/src/rv64gc/up_sigdeliver.c b/arch/risc-v/src/rv64gc/up_sigdeliver.c
index 187eb3f..f1c2b5c 100644
--- a/arch/risc-v/src/rv64gc/up_sigdeliver.c
+++ b/arch/risc-v/src/rv64gc/up_sigdeliver.c
@@ -84,6 +84,15 @@ void up_sigdeliver(void)
int saved_errno = rtcb->pterrno;
+#ifdef CONFIG_SMP
+ /* In the SMP case, we must terminate the critical section while the signal
+ * handler executes, but we also need to restore the irqcount when the
+ * we resume the main thread of the task.
+ */
+
+ int16_t saved_irqcount;
+#endif
+
board_autoled_on(LED_SIGNAL);
sinfo("rtcb=%p sigdeliver=%p sigpendactionq.head=%p\n",
@@ -94,6 +103,26 @@ void up_sigdeliver(void)
up_copystate(regs, rtcb->xcp.regs);
+#ifdef CONFIG_SMP
+ /* In the SMP case, up_schedule_sigaction(0) will have incremented
+ * 'irqcount' in order to force us into a critical section. Save the
+ * pre-incremented irqcount.
+ */
+
+ saved_irqcount = rtcb->irqcount - 1;
+ DEBUGASSERT(saved_irqcount >= 0);
+
+ /* Now we need call leave_critical_section() repeatedly to get the irqcount
+ * to zero, freeing all global spinlocks that enforce the critical section.
+ */
+
+ do
+ {
+ leave_critical_section(regs[REG_INT_CTX]);
+ }
+ while (rtcb->irqcount > 0);
+#endif /* CONFIG_SMP */
+
#ifndef CONFIG_SUPPRESS_INTERRUPTS
/* Then make sure that interrupts are enabled. Signal handlers must always
* run with interrupts enabled.
@@ -111,10 +140,28 @@ void up_sigdeliver(void)
* errno that is needed by the user logic (it is probably EINTR).
*/
- sinfo("Resuming EPC: %08x INT_CTX: %08x\n",
+ sinfo("Resuming EPC: %016x INT_CTX: %016x\n",
regs[REG_EPC], regs[REG_INT_CTX]);
+ /* Call enter_critical_section() to disable local interrupts before
+ * restoring local context.
+ *
+ * Here, we should not use up_irq_save() in SMP mode.
+ * For example, if we call up_irq_save() here and another CPU might
+ * have called up_cpu_pause() to this cpu, hence g_cpu_irqlock has
+ * been locked by the cpu, in this case, we would see a deadlock in
+ * later call of enter_critical_section() to restore irqcount.
+ * To avoid this situation, we need to call enter_critical_section().
+ */
+
+#ifdef CONFIG_SMP
+ (void)enter_critical_section();
+#else
(void)up_irq_save();
+#endif
+
+ /* Restore the saved errno value */
+
rtcb->pterrno = saved_errno;
/* Modify the saved return state with the actual saved values in the
@@ -131,16 +178,26 @@ void up_sigdeliver(void)
regs[REG_INT_CTX] = rtcb->xcp.saved_int_ctx;
rtcb->xcp.sigdeliver = NULL; /* Allows next handler to be scheduled */
+#ifdef CONFIG_SMP
+ /* Restore the saved 'irqcount' and recover the critical section
+ * spinlocks.
+ *
+ * REVISIT: irqcount should be one from the above call to
+ * enter_critical_section(). Could the saved_irqcount be zero? That
+ * would be a problem.
+ */
+
+ DEBUGASSERT(rtcb->irqcount == 1);
+ while (rtcb->irqcount < saved_irqcount)
+ {
+ (void)enter_critical_section();
+ }
+#endif
+
/* Then restore the correct state for this thread of
* execution.
*/
board_autoled_off(LED_SIGNAL);
up_fullcontextrestore(regs);
-
- /* up_fullcontextrestore() should not return but could if the software
- * interrupts are disabled.
- */
-
- DEBUGPANIC();
}
diff --git a/arch/risc-v/src/rv64gc/up_swint.c b/arch/risc-v/src/rv64gc/up_swint.c
index 0dfc921..59663a4 100644
--- a/arch/risc-v/src/rv64gc/up_swint.c
+++ b/arch/risc-v/src/rv64gc/up_swint.c
@@ -122,7 +122,7 @@ int up_swint(int irq, FAR void *context, FAR void *arg)
{
uint64_t *regs = (uint64_t *)context;
- DEBUGASSERT(regs && regs == g_current_regs);
+ DEBUGASSERT(regs && regs == CURRENT_REGS);
/* Software interrupt 0 is invoked with REG_A0 (REG_X10) = system call
* command and REG_A1-6 = variable number of
@@ -147,16 +147,16 @@ int up_swint(int irq, FAR void *context, FAR void *arg)
* A0 = SYS_restore_context
* A1 = restoreregs
*
- * In this case, we simply need to set g_current_regs to restore register
- * area referenced in the saved R1. context == g_current_regs is the normal
- * exception return. By setting g_current_regs = context[R1], we force
+ * In this case, we simply need to set CURRENT_REGS to restore register
+ * area referenced in the saved R1. context == CURRENT_REGS is the normal
+ * exception return. By setting CURRENT_REGS = context[R1], we force
* the return to the saved context referenced in $a1.
*/
case SYS_restore_context:
{
DEBUGASSERT(regs[REG_A1] != 0);
- g_current_regs = (uint64_t *)regs[REG_A1];
+ CURRENT_REGS = (uint64_t *)regs[REG_A1];
}
break;
@@ -172,7 +172,7 @@ int up_swint(int irq, FAR void *context, FAR void *arg)
*
* In this case, we save the context registers to the save register
* area referenced by the saved contents of R5 and then set
- * g_current_regs to the save register area referenced by the saved
+ * CURRENT_REGS to the save register area referenced by the saved
* contents of R6.
*/
@@ -180,7 +180,7 @@ int up_swint(int irq, FAR void *context, FAR void *arg)
{
DEBUGASSERT(regs[REG_A1] != 0 && regs[REG_A2] != 0);
up_copystate((uint64_t *)regs[REG_A1], regs);
- g_current_regs = (uint64_t *)regs[REG_A2];
+ CURRENT_REGS = (uint64_t *)regs[REG_A2];
}
break;
@@ -210,7 +210,7 @@ int up_swint(int irq, FAR void *context, FAR void *arg)
* the original mode.
*/
- g_current_regs[REG_EPC] = rtcb->xcp.syscall[index].sysreturn;
+ CURRENT_REGS[REG_EPC] = rtcb->xcp.syscall[index].sysreturn;
#error "Missing logic -- need to restore the original mode"
rtcb->xcp.nsyscalls = index;
@@ -237,7 +237,7 @@ int up_swint(int irq, FAR void *context, FAR void *arg)
/* Verify that the SYS call number is within range */
- DEBUGASSERT(g_current_regs[REG_A0] < SYS_maxsyscall);
+ DEBUGASSERT(CURRENT_REGS[REG_A0] < SYS_maxsyscall);
/* Make sure that we got here that there is a no saved syscall
* return address. We cannot yet handle nested system calls.
@@ -256,7 +256,7 @@ int up_swint(int irq, FAR void *context, FAR void *arg)
/* Offset R0 to account for the reserved values */
- g_current_regs[REG_A0] -= CONFIG_SYS_RESERVED;
+ CURRENT_REGS[REG_A0] -= CONFIG_SYS_RESERVED;
/* Indicate that we are in a syscall handler. */
@@ -271,10 +271,10 @@ int up_swint(int irq, FAR void *context, FAR void *arg)
/* Report what happened. That might difficult in the case of a context switch */
#ifdef CONFIG_DEBUG_SYSCALL_INFO
- if (regs != g_current_regs)
+ if (regs != CURRENT_REGS)
{
svcinfo("SWInt Return: Context switch!\n");
- up_registerdump((const uint32_t *)g_current_regs);
+ up_registerdump((const uint32_t *)CURRENT_REGS);
}
else
{
diff --git a/arch/risc-v/src/k210/k210_timerisr.c b/arch/risc-v/src/rv64gc/up_testset.S
similarity index 63%
copy from arch/risc-v/src/k210/k210_timerisr.c
copy to arch/risc-v/src/rv64gc/up_testset.S
index 80c3652..688e445 100644
--- a/arch/risc-v/src/k210/k210_timerisr.c
+++ b/arch/risc-v/src/rv64gc/up_testset.S
@@ -1,9 +1,11 @@
/****************************************************************************
- * arch/risc-v/src/k210/k210_timerisr.c
+ * arch/risc-v/src/rv64gc/up_testset.S
*
- * Copyright (C) 2019 Masayuki Ishikawa. All rights reserved.
+ * Copyright (C) 2020 Masayuki Ishikawa. All rights reserved.
* Author: Masayuki Ishikawa <ma...@gmail.com>
*
+ * Based on arch/arm/src/armv7-m/gnu/up_testset.S
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -38,108 +40,84 @@
****************************************************************************/
#include <nuttx/config.h>
+#include <arch/spinlock.h>
-#include <stdint.h>
-#include <time.h>
-#include <debug.h>
-
-#include <nuttx/arch.h>
-#include <arch/board/board.h>
-
-#include "up_arch.h"
-
-#include "k210.h"
-#include "k210_clockconfig.h"
+ .file "arm_testset.S"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
-#define getreg64(a) (*(volatile uint64_t *)(a))
-#define putreg64(v,a) (*(volatile uint64_t *)(a) = (v))
-
-#define TICK_COUNT ((k210_get_cpuclk() / 50) / TICK_PER_SEC)
-
/****************************************************************************
- * Private Data
+ * Public Symbols
****************************************************************************/
-static bool _b_tick_started = false;
+ .globl up_testset
/****************************************************************************
- * Private Functions
+ * Assembly Macros
****************************************************************************/
/****************************************************************************
- * Name: k210_reload_mtimecmp
- ****************************************************************************/
-
-static void k210_reload_mtimecmp(void)
-{
- irqstate_t flags = spin_lock_irqsave();
-
- uint64_t current;
- uint64_t next;
-
- if (!_b_tick_started)
- {
- _b_tick_started = true;
- current = getreg64(K210_CLINT_MTIME);
- }
- else
- {
- current = getreg64(K210_CLINT_MTIMECMP);
- }
-
- uint64_t tick = TICK_COUNT;
- next = current + tick;
-
- putreg64(next, K210_CLINT_MTIMECMP);
-
- spin_unlock_irqrestore(flags);
-}
-
-/****************************************************************************
- * Name: k210_timerisr
+ * Private Functions
****************************************************************************/
-static int k210_timerisr(int irq, void *context, FAR void *arg)
-{
- k210_reload_mtimecmp();
-
- /* Process timer interrupt */
-
- nxsched_process_timer();
- return 0;
-}
+ .text
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
- * Name: riscv_timer_initialize
+ * Name: up_testset
*
* Description:
- * This function is called during start-up to initialize
- * the timer interrupt.
+ * Perform an atomic test and set operation on the provided spinlock.
+ *
+ * This function must be provided via the architecture-specific logic.
+ *
+ * Input Parameters:
+ * lock - The address of spinlock object (a0).
+ *
+ * Returned Value:
+ * The spinlock is always locked upon return. The value of previous value
+ * of the spinlock variable is returned, either SP_LOCKED if the spinlock
+ * as previously locked (meaning that the test-and-set operation failed to
+ * obtain the lock) or SP_UNLOCKED if the spinlock was previously unlocked
+ * (meaning that we successfully obtained the lock)
+ *
+ * Modifies: a1, a2
*
****************************************************************************/
-void riscv_timer_initialize(void)
-{
-#if 1
- /* Attach timer interrupt handler */
+ .globl up_testset
+ .type up_testset, %function
+
+up_testset:
+
+ li a1, SP_LOCKED
+
+ /* Test if the spinlock is locked or not */
+
+retry:
+ lr.d a2, (a0) /* Test if spinlock is locked or not */
+ beq a2, a1, locked /* Already locked? Go to locked: */
- (void)irq_attach(K210_IRQ_MTIMER, k210_timerisr, NULL);
+ /* Not locked ... attempt to lock it */
- /* Reload CLINT mtimecmp */
+ sc.d a2, a1, (a0) /* Attempt to set the locked state (a1) to (a0) */
+ bnez a2, retry /* a2 will not be zero, if sc.b failed, try again */
- k210_reload_mtimecmp();
+ /* Lock acquired -- return SP_UNLOCKED */
- /* And enable the timer interrupt */
+ fence /* Required before accessing protected resource */
+ li a0, SP_UNLOCKED
+ jr ra
- up_enable_irq(K210_IRQ_MTIMER);
-#endif
-}
+ /* Lock not acquired -- return SP_LOCKED */
+locked:
+ li a0, SP_LOCKED
+ jr ra
+ .size up_testset, . - up_testset
+ .end
diff --git a/arch/risc-v/src/rv64gc/up_unblocktask.c b/arch/risc-v/src/rv64gc/up_unblocktask.c
index 70b0e59..ae0f1c9 100644
--- a/arch/risc-v/src/rv64gc/up_unblocktask.c
+++ b/arch/risc-v/src/rv64gc/up_unblocktask.c
@@ -100,10 +100,10 @@ void up_unblock_task(struct tcb_s *tcb)
/* Are we in an interrupt handler? */
- if (g_current_regs)
+ if (CURRENT_REGS)
{
/* Yes, then we have to do things differently.
- * Just copy the g_current_regs into the OLD rtcb.
+ * Just copy the CURRENT_REGS into the OLD rtcb.
*/
up_savestate(rtcb->xcp.regs);
diff --git a/boards/risc-v/k210/maix-bit/README-qemu.txt b/boards/risc-v/k210/maix-bit/README-qemu.txt
new file mode 100644
index 0000000..65a5040
--- /dev/null
+++ b/boards/risc-v/k210/maix-bit/README-qemu.txt
@@ -0,0 +1,43 @@
+1. Download and install toolchain
+
+ $ curl https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14.tar.gz
+
+2. Build and install qemu
+
+ $ git clone https://github.com/qemu/qemu
+ $ cd qemu
+ $ ./configure --target-list=riscv64-softmmu
+ $ make
+ $ sudo make install
+
+3. Modify defconfig
+
+--- a/boards/risc-v/k210/maix-bit/configs/nsh/defconfig
++++ b/boards/risc-v/k210/maix-bit/configs/nsh/defconfig
+@@ -25,6 +25,7 @@ CONFIG_EXAMPLES_HELLO=y
+ CONFIG_FS_PROCFS=y
+ CONFIG_IDLETHREAD_STACKSIZE=2048
+ CONFIG_INTELHEX_BINARY=y
++CONFIG_K210_WITH_QEMU=y
+ CONFIG_LIBC_PERROR_STDOUT=y
+ CONFIG_LIBC_STRERROR=y
+ CONFIG_MAX_TASKS=64
+
+4. Configure and build NuttX
+
+ $ mkdir ./nuttx; cd ./nuttx
+ $ git clone https://bitbucket.org/nuttx/nuttx.git
+ $ git clone https://bitbucket.org/nuttx/apps.git
+ $ cd nuttx
+ $ make distclean
+ $ ./tools/configure.sh maix-bit:nsh
+ $ make V=1
+
+5. Run the nuttx with qemu
+
+ $ qemu-system-riscv64 -nographic -machine sifive_u -bios ./nuttx
+
+6. TODO
+
+ Support FPU
+ Support RISC-V User mode
diff --git a/boards/risc-v/k210/maix-bit/README.txt b/boards/risc-v/k210/maix-bit/README.txt
index b240d83..5d04058 100644
--- a/boards/risc-v/k210/maix-bit/README.txt
+++ b/boards/risc-v/k210/maix-bit/README.txt
@@ -32,4 +32,5 @@
PLL setting (currently CPU clock freq is assumed to be 416MHz)
Boot from SPI-Flash
Support peripherals such as GPIO/SPI/I2C/...
+ Support FPU
Support RISC-V User mode
diff --git a/boards/risc-v/k210/maix-bit/configs/nsh/defconfig b/boards/risc-v/k210/maix-bit/configs/nsh/defconfig
index e8ceae4..41e9127 100644
--- a/boards/risc-v/k210/maix-bit/configs/nsh/defconfig
+++ b/boards/risc-v/k210/maix-bit/configs/nsh/defconfig
@@ -57,9 +57,7 @@ CONFIG_TASK_NAME_SIZE=20
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_GETPRIME_STACKSIZE=2048
CONFIG_TESTING_OSTEST=y
-CONFIG_UART0_RXBUFSIZE=8
CONFIG_UART0_SERIAL_CONSOLE=y
-CONFIG_UART0_TXBUFSIZE=8
CONFIG_USERMAIN_STACKSIZE=3072
CONFIG_USER_ENTRYPOINT="nsh_main"
CONFIG_WDOG_INTRESERVE=0
diff --git a/boards/risc-v/k210/maix-bit/configs/nsh/defconfig b/boards/risc-v/k210/maix-bit/configs/smp/defconfig
similarity index 80%
copy from boards/risc-v/k210/maix-bit/configs/nsh/defconfig
copy to boards/risc-v/k210/maix-bit/configs/smp/defconfig
index e8ceae4..c275c49 100644
--- a/boards/risc-v/k210/maix-bit/configs/nsh/defconfig
+++ b/boards/risc-v/k210/maix-bit/configs/smp/defconfig
@@ -18,17 +18,20 @@ CONFIG_ARCH_STACKDUMP=y
CONFIG_BINFMT_DISABLE=y
CONFIG_BOARD_LOOPSPERMSEC=15000
CONFIG_BUILTIN=y
+CONFIG_BUILTIN_PROXY_STACKSIZE=2048
CONFIG_DEBUG_FULLOPT=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_ZERO=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_FS_PROCFS=y
+CONFIG_FS_PROCFS_REGISTER=y
CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBC_PERROR_STDOUT=y
CONFIG_LIBC_STRERROR=y
CONFIG_MAX_TASKS=64
CONFIG_MAX_WDOGPARMS=2
+CONFIG_NFILE_DESCRIPTORS=8
CONFIG_NFILE_STREAMS=8
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_BUILTIN_APPS=y
@@ -40,6 +43,8 @@ CONFIG_NSH_DISABLE_UMOUNT=y
CONFIG_NSH_FILEIOSIZE=64
CONFIG_NSH_READLINE=y
CONFIG_NSH_STRERROR=y
+CONFIG_POSIX_SPAWN_PROXY_STACKSIZE=2048
+CONFIG_PREALLOC_MQ_MSGS=4
CONFIG_PREALLOC_TIMERS=4
CONFIG_PREALLOC_WDOGS=16
CONFIG_RAM_SIZE=2097152
@@ -47,19 +52,23 @@ CONFIG_RAM_START=0x80400000
CONFIG_RAW_BINARY=y
CONFIG_READLINE_CMD_HISTORY=y
CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_INSTRUMENTATION=y
+CONFIG_SCHED_INSTRUMENTATION_BUFFER=y
CONFIG_SCHED_WAITPID=y
+CONFIG_SMP=y
+CONFIG_SMP_NCPUS=2
+CONFIG_SPINLOCK_IRQ=y
CONFIG_STACK_COLORATION=y
-CONFIG_START_DAY=28
-CONFIG_START_MONTH=12
-CONFIG_START_YEAR=2019
+CONFIG_START_DAY=8
+CONFIG_START_MONTH=1
+CONFIG_START_YEAR=2020
CONFIG_SYSTEM_NSH=y
+CONFIG_SYSTEM_TASKSET=y
CONFIG_TASK_NAME_SIZE=20
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_GETPRIME_STACKSIZE=2048
CONFIG_TESTING_OSTEST=y
-CONFIG_UART0_RXBUFSIZE=8
+CONFIG_TESTING_SMP=y
CONFIG_UART0_SERIAL_CONSOLE=y
-CONFIG_UART0_TXBUFSIZE=8
CONFIG_USERMAIN_STACKSIZE=3072
CONFIG_USER_ENTRYPOINT="nsh_main"
-CONFIG_WDOG_INTRESERVE=0