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