This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 5f2dadf17d519caf2374501650148fe721a0461e
Author: Alan Carvalho de Assis <[email protected]>
AuthorDate: Sat Oct 24 21:23:42 2026 -0300

    arch/risc-v/ab32vg1: add Bluetrum AB32VG1 chip support
    
    Add support for the Bluetrum AB32VG1 (AB5301A, "CRWN"), an RV32IMC
    Bluetooth audio SoC with 192 KB of RAM and 1 MB of in-package flash.
    
    The AB32VG1 core implements the RISC-V unprivileged ISA but not the
    standard machine-mode trap architecture: there are no CSRs (any CSR
    access, ecall or ebreak raises an exception it cannot resume from) and
    interrupts are controlled through memory-mapped registers.  Add a hidden
    ARCH_RV_CUSTOM_TRAP option that lets such a chip provide
    up_irq_save()/up_irq_restore() and the system call instruction through
    <arch/chip/trap.h>, and its own trap entry, up_saveusercontext() and
    initial interrupt context instead of the CSR-based common files.  No
    functional change for other chips.
    
    AB32VG1 support:
    - Trap entry through a RAM vector table (PICADR); EPC/EPICCON are
      memory-mapped and mret restores the interrupt enable, also from thread
      mode.  System calls use a software trap that builds the same frame as
      a hardware trap with interrupts masked.  Exceptions are reported and
      fatal.
    - System clock (PLL0 / n, 48 MHz default), TMR0 system tick, interrupt
      controller, UART0 console in single-wire mode on PA7 with an optional
      key that resets the chip into the mask ROM UART loader, GPIO and
      watchdog reset.
    - The image runs from RAM; the heap spreads over SRAM, AECRAM and ICRAM.
    
    Add the chip documentation: memory map, trap architecture, boot process
    and the mask ROM's CPU register 0x508.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Alan Carvalho de Assis <[email protected]>
---
 Documentation/platforms/risc-v/ab32vg1/index.rst   | 113 ++++
 arch/risc-v/Kconfig                                |  35 ++
 arch/risc-v/include/ab32vg1/chip.h                 |  47 ++
 arch/risc-v/include/ab32vg1/irq.h                  | 170 ++++++
 arch/risc-v/include/irq.h                          |   6 +-
 arch/risc-v/include/syscall.h                      |  24 +-
 arch/risc-v/src/ab32vg1/CMakeLists.txt             |  41 ++
 arch/risc-v/src/ab32vg1/Kconfig                    |  39 ++
 arch/risc-v/src/ab32vg1/Make.defs                  |  33 ++
 arch/risc-v/src/ab32vg1/ab32vg1.h                  | 116 ++++
 arch/risc-v/src/ab32vg1/ab32vg1_allocateheap.c     | 133 +++++
 arch/risc-v/src/ab32vg1/ab32vg1_clockconfig.c      | 111 ++++
 arch/risc-v/src/ab32vg1/ab32vg1_gpio.c             | 155 +++++
 arch/risc-v/src/ab32vg1/ab32vg1_gpio.h             | 116 ++++
 arch/risc-v/src/ab32vg1/ab32vg1_head.S             | 297 ++++++++++
 arch/risc-v/src/ab32vg1/ab32vg1_idle.c             |  61 ++
 arch/risc-v/src/ab32vg1/ab32vg1_irq.c              | 226 ++++++++
 arch/risc-v/src/ab32vg1/ab32vg1_lowputc.c          | 142 +++++
 arch/risc-v/src/ab32vg1/ab32vg1_lowputc.h          |  51 ++
 arch/risc-v/src/ab32vg1/ab32vg1_serial.c           | 631 +++++++++++++++++++++
 arch/risc-v/src/ab32vg1/ab32vg1_start.c            | 108 ++++
 arch/risc-v/src/ab32vg1/ab32vg1_systemreset.c      |  54 ++
 arch/risc-v/src/ab32vg1/ab32vg1_timerisr.c         |  95 ++++
 arch/risc-v/src/ab32vg1/chip.h                     |  38 ++
 arch/risc-v/src/ab32vg1/hardware/ab32vg1_clk.h     | 112 ++++
 arch/risc-v/src/ab32vg1/hardware/ab32vg1_gpio.h    |  81 +++
 .../src/ab32vg1/hardware/ab32vg1_memorymap.h       |  47 ++
 arch/risc-v/src/ab32vg1/hardware/ab32vg1_pic.h     |  70 +++
 arch/risc-v/src/ab32vg1/hardware/ab32vg1_timer.h   |  66 +++
 arch/risc-v/src/ab32vg1/hardware/ab32vg1_uart.h    |  76 +++
 arch/risc-v/src/common/CMakeLists.txt              |   8 +-
 arch/risc-v/src/common/Make.defs                   |  12 +-
 32 files changed, 3301 insertions(+), 13 deletions(-)

diff --git a/Documentation/platforms/risc-v/ab32vg1/index.rst 
b/Documentation/platforms/risc-v/ab32vg1/index.rst
new file mode 100644
index 00000000000..11c8ca41fd0
--- /dev/null
+++ b/Documentation/platforms/risc-v/ab32vg1/index.rst
@@ -0,0 +1,113 @@
+================
+Bluetrum AB32VG1
+================
+
+.. tags:: arch:risc-v, chip:ab32vg1, vendor:bluetrum
+
+The Bluetrum AB32VG1 (also sold as AB5301A, code name "CRWN") is a Bluetooth
+audio SoC with a 32-bit RISC-V core:
+
+* RV32IMC core, up to 120 MHz (48 MHz by default in NuttX)
+* 192 KB of RAM in three blocks:
+
+  ============  ===========  =========  ==============================
+  Name          Address      Size       Use in NuttX
+  ============  ===========  =========  ==============================
+  SRAM          0x00010000   128 KB     image (from 0x12000), heap
+  AECRAM        0x00050000   30.5 KB    libc/mm/driver code, heap
+  ICRAM         0x00060000   32 KB      .bss, idle stack, heap
+  ============  ===========  =========  ==============================
+
+* 1 MB SPI NOR flash in the package (SPI0, port G)
+* 3 UARTs, 6 timers, GPIO, RTC, watchdog, SD host, USB, audio codec, FM
+  radio, Bluetooth
+
+There is no public reference manual for the CPU core and interrupt
+controller.  The `register manual
+<https://github.com/BLUETRUM/AB32VG1_DOC>`_ covers most peripherals; the
+rest of the information used by this port comes from the RT-Thread BSP
+(``bsp/bluetrum``) and from experiments on the hardware, documented below.
+
+Core and interrupts
+===================
+
+The core implements the RISC-V unprivileged ISA (RV32IMC) but **not** the
+standard machine-mode trap architecture:
+
+* There are no CSRs.  Any CSR instruction (including ``rdcycle``), ``ecall``
+  or ``ebreak`` raises an exception, and after an exception the core traps
+  again on every following instruction.  Exceptions are therefore fatal and
+  the port never executes these instructions (images can be checked with
+  ``riscv-none-elf-objdump -d nuttx | grep csr``).
+* Trap state and interrupt control are memory-mapped registers: ``PICCON``
+  (bit 0: interrupt enable, bit 1: controller enable), ``PICEN`` (one enable
+  bit per source), ``PICPR`` (priority), ``PICADR`` (vector table base),
+  ``PICPND`` (pending), ``EPC`` and ``EPICCON`` (``PICCON.IE`` saved on
+  trap).
+* The vector table has fixed offsets from ``PICADR``: +0x10 exceptions and
+  high priority interrupts, +0x40 low priority interrupts, +0x80 XIP cache
+  misses.  NuttX uses low priority interrupts only.
+* ``mret`` returns to ``EPC`` and restores ``PICCON.IE`` from ``EPICCON``;
+  this also works outside of a trap.  Low priority interrupts do not nest.
+
+The chip selects ``CONFIG_ARCH_RV_CUSTOM_TRAP``, which lets a chip replace
+the CSR-based parts of the common RISC-V code: ``up_irq_save()`` and
+``up_irq_restore()`` operate on ``PICCON``, and system calls (used for
+context switches) use a *software trap*: ``sys_callN()`` calls
+``ab32vg1_syscall_trap()``, which builds the same register frame as a
+hardware trap with interrupts masked, dispatches it like an ``ecall`` and
+returns through the common trap exit (``EPC``/``EPICCON`` + ``mret``).
+
+Memory and boot
+===============
+
+Code normally runs from RAM.  The mask ROM can execute from flash through a
+software-managed instruction cache (that is how RT-Thread runs), but every
+interrupt handler and everything it calls must then live in RAM, which does
+not suit NuttX.  NuttX is therefore loaded into RAM entirely:
+
+* by the mask ROM UART loader, for development (``ab32vg1_dl run``), or
+* from flash by a small stage-1 loader started by the mask ROM
+  (``ab32vg1_dl flash``).
+
+The mask ROM copies the boot code named by the boot header of the flash
+(scrambled, CRC-checked) to RAM and calls it.  The boot code must clear CPU
+register 0x508 early: otherwise the mask ROM takes control back, into its
+UART loader, some tens of milliseconds later.  That is also the only way
+the UART loader is reached after a reset once a boot header is present, so
+boot code must listen for the host before clearing 0x508 (see the board
+documentation).
+
+The ROM loader can only write SRAM up to 0x2e800 (it keeps its own data
+above), so the image is split: code of the C library, memory manager and
+drivers goes to AECRAM, ``.bss`` and the idle stack to ICRAM.  The heap uses
+all remaining RAM (about 72 KB with the ``nsh`` configuration).
+
+Supported Peripherals
+=====================
+
+==========  =======  =====================================================
+Peripheral  Support  Notes
+==========  =======  =====================================================
+Clock       Yes      PLL0 (240 MHz) / n, ``CONFIG_AB32VG1_SYSCLK_MHZ``
+Timer 0     Yes      System tick
+UART0       Yes      Single-wire on PA7, console
+GPIO        Yes      No pin interrupts yet
+Watchdog    Reset    Used for ``up_systemreset()``
+Flash       Tools    Programmed by ``ab32vg1_dl``, no MTD driver yet
+UART1/2     No
+SPI, I2C    No
+RTC         No
+SD          No
+USB         No
+Audio, BT   No
+==========  =======  =====================================================
+
+Supported Boards
+================
+
+.. toctree::
+   :glob:
+   :maxdepth: 1
+
+   boards/*/*
diff --git a/arch/risc-v/Kconfig b/arch/risc-v/Kconfig
index 9f62fa15d4c..07d04614435 100644
--- a/arch/risc-v/Kconfig
+++ b/arch/risc-v/Kconfig
@@ -69,6 +69,19 @@ config ARCH_CHIP_GD32VW55X
                GigaDevice GD32VW55x Wi-Fi/BLE SoC (Nuclei N307 rv32imafc,
                ECLIC interrupt controller, 160 MHz, 320 KB SRAM).
 
+config ARCH_CHIP_AB32VG1
+       bool "Bluetrum AB32VG1"
+       select ARCH_RV32
+       select ARCH_RV_ISA_M
+       select ARCH_RV_ISA_C
+       select ARCH_RV_CUSTOM_TRAP
+       select ARCH_HAVE_RESET
+       select ARCH_IDLE_CUSTOM
+       select LIBC_ATOMIC_IRQ
+       ---help---
+               Bluetrum AB32VG1 (AB5301A, "CRWN") Bluetooth audio SoC with a
+               rv32imc core, 192 KB SRAM and 1 MB SPI flash.
+
 config ARCH_CHIP_BL602
        bool "BouffaloLab BL602"
        select ARCH_RV32
@@ -716,6 +729,7 @@ config ARCH_CHIP
        default "k210"                  if ARCH_CHIP_K210
        default "litex"                 if ARCH_CHIP_LITEX
        default "gd32vw55x"             if ARCH_CHIP_GD32VW55X
+       default "ab32vg1"               if ARCH_CHIP_AB32VG1
        default "bl602"                 if ARCH_CHIP_BL602
        default "esp32c3-legacy"        if ARCH_CHIP_ESP32C3_LEGACY
        default "esp32c2"               if ARCH_CHIP_ESP32C2
@@ -829,6 +843,24 @@ config ARCH_RV_HAVE_APLIC
                Controller (APLIC) to provide flexible interrupt control. This 
device
                is not backward compatible with PLIC.
 
+config ARCH_RV_CUSTOM_TRAP
+       bool
+       default n
+       ---help---
+               Selected by chips whose core implements the RISC-V unprivileged 
ISA
+               but not the standard machine-mode trap architecture: there are 
no
+               mstatus/mtvec/mepc/mcause CSRs and ecall does not trap.  The 
chip
+               then provides:
+
+               - up_irq_save() and up_irq_restore() in <arch/chip/irq.h>.
+               - RISCV_SYSCALL_INSN in <arch/chip/irq.h>: the instruction that
+                 sys_callN() uses instead of ecall.  It must trap synchronously
+                 with interrupts masked and be dispatched as RISCV_IRQ_ECALLM.
+               - The trap entry, riscv_get_newintctx(), riscv_set_idleintctx() 
and
+                 up_saveusercontext(); the common versions are not built.  The
+                 saved REG_INT_CTX must use the mstatus bits (STATUS_PIE,
+                 STATUS_PPP) so that the common code can keep using them.
+
 config ARCH_RV_HAVE_CLIC
        bool
        default n
@@ -962,6 +994,9 @@ endif
 if ARCH_CHIP_GD32VW55X
 source "arch/risc-v/src/gd32vw55x/Kconfig"
 endif
+if ARCH_CHIP_AB32VG1
+source "arch/risc-v/src/ab32vg1/Kconfig"
+endif
 if ARCH_CHIP_BL602
 source "arch/risc-v/src/bl602/Kconfig"
 endif
diff --git a/arch/risc-v/include/ab32vg1/chip.h 
b/arch/risc-v/include/ab32vg1/chip.h
new file mode 100644
index 00000000000..4779b0493d1
--- /dev/null
+++ b/arch/risc-v/include/ab32vg1/chip.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+ * arch/risc-v/include/ab32vg1/chip.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_INCLUDE_AB32VG1_CHIP_H
+#define __ARCH_RISCV_INCLUDE_AB32VG1_CHIP_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Memory map (AB32VG1 datasheet, "Memory mapping"):
+ *
+ *   0x00000000 - 0x00000fff  SFR space (accessed with x0-relative loads)
+ *   0x00010000 - 0x0002ffff  SRAM0..3, 128 KB
+ *   0x00050000 - 0x000579ff  AECRAM/DECRAM/ENCRAM, 30.5 KB
+ *   0x00060000 - 0x00067fff  ICRAM (XIP cache lines), 32 KB
+ *   0x00080000 - ...         Mask ROM
+ *   0x10000000 - 0x100fffff  SPI flash window (software-cached XIP)
+ */
+
+#define AB32VG1_SRAM_BASE     0x00010000
+#define AB32VG1_SRAM_SIZE     0x00020000
+#define AB32VG1_AECRAM_BASE   0x00050000
+#define AB32VG1_AECRAM_SIZE   0x00007a00
+#define AB32VG1_ICRAM_BASE    0x00060000
+#define AB32VG1_ICRAM_SIZE    0x00008000
+
+#endif /* __ARCH_RISCV_INCLUDE_AB32VG1_CHIP_H */
diff --git a/arch/risc-v/include/ab32vg1/irq.h 
b/arch/risc-v/include/ab32vg1/irq.h
new file mode 100644
index 00000000000..66fc3423ca8
--- /dev/null
+++ b/arch/risc-v/include/ab32vg1/irq.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+ * arch/risc-v/include/ab32vg1/irq.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* The AB32VG1 core implements RV32IMC but not the standard machine-mode
+ * trap architecture (CONFIG_ARCH_RV_CUSTOM_TRAP):
+ *
+ * - There are no CSRs.  Any CSR access, ecall or ebreak raises an
+ *   exception which leaves the core in a state where every following
+ *   instruction traps again, so these instructions must never be used.
+ * - Interrupt masking is bit 0 of the memory-mapped PICCON register.  On
+ *   trap entry the previous value is saved in EPICCON and mret restores it.
+ *
+ * System calls therefore use a software trap: RISCV_SYSCALL_INSN calls
+ * ab32vg1_syscall_trap(), which builds the same register frame as a
+ * hardware trap with interrupts masked, dispatches it as RISCV_IRQ_ECALLM
+ * and leaves through the common trap exit (EPC/EPICCON + mret), which also
+ * works from thread mode.  ra is preserved on the stack so that the
+ * instruction sequence does not clobber any register.
+ */
+
+#ifndef __ARCH_RISCV_INCLUDE_AB32VG1_IRQ_H
+#define __ARCH_RISCV_INCLUDE_AB32VG1_IRQ_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifndef __ASSEMBLY__
+#  include <sys/types.h>
+#  include <nuttx/compiler.h>
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The AB32VG1 interrupt controller (PIC) has one enable/pending bit per
+ * source.  Several peripherals share a source.  NuttX IRQ numbers follow
+ * the RISC-V exception numbers (0..RISCV_MAX_EXCEPTION).
+ */
+
+#define AB32VG1_IRQ_FIRST       RISCV_IRQ_ASYNC
+
+#define AB32VG1_IRQ_SW          (AB32VG1_IRQ_FIRST + 2)  /* Software */
+#define AB32VG1_IRQ_TMR0        (AB32VG1_IRQ_FIRST + 3)  /* Timer 0 */
+#define AB32VG1_IRQ_TMR1        (AB32VG1_IRQ_FIRST + 4)  /* Timer 1 */
+#define AB32VG1_IRQ_TMR245      (AB32VG1_IRQ_FIRST + 5)  /* Timer 2, 4, 5 */
+#define AB32VG1_IRQ_IRRX        (AB32VG1_IRQ_FIRST + 6)  /* Timer 3, IR rx */
+#define AB32VG1_IRQ_USB         (AB32VG1_IRQ_FIRST + 7)  /* USB */
+#define AB32VG1_IRQ_SD          (AB32VG1_IRQ_FIRST + 8)  /* SD host */
+#define AB32VG1_IRQ_AUBUF       (AB32VG1_IRQ_FIRST + 9)  /* Audio buffer 0/1 */
+#define AB32VG1_IRQ_SDADC       (AB32VG1_IRQ_FIRST + 10) /* SD ADC */
+#define AB32VG1_IRQ_AUDEC       (AB32VG1_IRQ_FIRST + 11) /* Audio codec */
+#define AB32VG1_IRQ_SRC         (AB32VG1_IRQ_FIRST + 12) /* SRC, PLC, CVSD */
+#define AB32VG1_IRQ_FM          (AB32VG1_IRQ_FIRST + 13) /* FM TX/RX, SPDIF */
+#define AB32VG1_IRQ_UART        (AB32VG1_IRQ_FIRST + 14) /* UART 0, 1, 2 */
+#define AB32VG1_IRQ_HSUART      (AB32VG1_IRQ_FIRST + 15) /* High speed UART */
+#define AB32VG1_IRQ_RTC         (AB32VG1_IRQ_FIRST + 16) /* RTC, LVD, WDT */
+#define AB32VG1_IRQ_I2S         (AB32VG1_IRQ_FIRST + 17) /* I2S */
+#define AB32VG1_IRQ_GPIO        (AB32VG1_IRQ_FIRST + 18) /* Port interrupt */
+
+#define AB32VG1_NPIC_IRQS       23
+
+#define NR_IRQS                 (AB32VG1_IRQ_FIRST + AB32VG1_NPIC_IRQS)
+
+/* Interrupt controller registers used by the inline functions below */
+
+#define AB32VG1_PICCONCLR_ADDR  0x00000530
+#define AB32VG1_PICCONSET_ADDR  0x00000534
+#define AB32VG1_PICCON_ADDR     0x00000540
+#define AB32VG1_PICCON_IE       (1 << 0)
+
+/* Software trap used by sys_callN() in place of ecall */
+
+#define RISCV_SYSCALL_INSN \
+  "addi sp, sp, -16\n" \
+  "sw   ra, 0(sp)\n" \
+  "jal  ra, ab32vg1_syscall_trap\n" \
+  "lw   ra, 0(sp)\n" \
+  "addi sp, sp, 16\n"
+
+#ifndef __ASSEMBLY__
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_irq_save
+ *
+ * Description:
+ *   Disable interrupts and return the previous interrupt enable state.
+ *
+ ****************************************************************************/
+
+noinstrument_function static inline_function irqstate_t up_irq_save(void)
+{
+  irqstate_t flags;
+
+  /* The SFRs are below 4 KB: address them relative to x0 */
+
+  __asm__ __volatile__
+    (
+      "lw   %0, %1(zero)\n"
+      "sw   %2, %3(zero)\n"
+      : "=&r" (flags)
+      : "i" (AB32VG1_PICCON_ADDR), "r" (AB32VG1_PICCON_IE),
+        "i" (AB32VG1_PICCONCLR_ADDR)
+      : "memory"
+    );
+
+  return flags & AB32VG1_PICCON_IE;
+}
+
+/****************************************************************************
+ * Name: up_irq_restore
+ *
+ * Description:
+ *   Restore the interrupt enable state returned by up_irq_save().
+ *
+ ****************************************************************************/
+
+noinstrument_function static inline_function
+void up_irq_restore(irqstate_t flags)
+{
+  if (flags & AB32VG1_PICCON_IE)
+    {
+      __asm__ __volatile__
+        (
+          "sw   %0, %1(zero)\n"
+          :
+          : "r" (AB32VG1_PICCON_IE), "i" (AB32VG1_PICCONSET_ADDR)
+          : "memory"
+        );
+    }
+  else
+    {
+      __asm__ __volatile__
+        (
+          "sw   %0, %1(zero)\n"
+          :
+          : "r" (AB32VG1_PICCON_IE), "i" (AB32VG1_PICCONCLR_ADDR)
+          : "memory"
+        );
+    }
+}
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_INCLUDE_AB32VG1_IRQ_H */
diff --git a/arch/risc-v/include/irq.h b/arch/risc-v/include/irq.h
index f631f7ab71a..3794009d3bf 100644
--- a/arch/risc-v/include/irq.h
+++ b/arch/risc-v/include/irq.h
@@ -802,7 +802,11 @@ int up_this_cpu(void);
  * Inline Functions
  ****************************************************************************/
 
-#ifdef CONFIG_ARCH_RV_HAVE_CLIC
+#if defined(CONFIG_ARCH_RV_CUSTOM_TRAP)
+
+/* up_irq_save() and up_irq_restore() are provided by <arch/chip/irq.h> */
+
+#elif defined(CONFIG_ARCH_RV_HAVE_CLIC)
 
 /****************************************************************************
  * Name: up_irq_save
diff --git a/arch/risc-v/include/syscall.h b/arch/risc-v/include/syscall.h
index d81f6684d05..2089341febb 100644
--- a/arch/risc-v/include/syscall.h
+++ b/arch/risc-v/include/syscall.h
@@ -150,6 +150,16 @@ uintptr_t sys_call6(unsigned int nbr, uintptr_t parm1, 
uintptr_t parm2,
  * in; otherwise there is no fp-chain consumer to fix up for.
  */
 
+/* The instruction that enters the kernel.  Cores without the standard trap
+ * architecture (CONFIG_ARCH_RV_CUSTOM_TRAP) provide their own.
+ */
+
+#ifdef CONFIG_ARCH_RV_CUSTOM_TRAP
+#  include <arch/chip/irq.h>
+#else
+#  define RISCV_SYSCALL_INSN "ecall"
+#endif
+
 #if defined(CONFIG_FRAME_POINTER) && defined(CONFIG_SCHED_BACKTRACE)
 #  define RISCV_ECALL_CLOBBERS "memory", "ra"
 #else
@@ -170,7 +180,7 @@ static inline uintptr_t sys_call0(unsigned int nbr)
 
   asm volatile
     (
-     "ecall"
+     RISCV_SYSCALL_INSN
      :: "r"(r0)
      : RISCV_ECALL_CLOBBERS
      );
@@ -195,7 +205,7 @@ static inline uintptr_t sys_call1(unsigned int nbr, 
uintptr_t parm1)
 
   asm volatile
     (
-     "ecall"
+     RISCV_SYSCALL_INSN
      :: "r"(r0), "r"(r1)
      : RISCV_ECALL_CLOBBERS
      );
@@ -222,7 +232,7 @@ static inline uintptr_t sys_call2(unsigned int nbr, 
uintptr_t parm1,
 
   asm volatile
     (
-     "ecall"
+     RISCV_SYSCALL_INSN
      :: "r"(r0), "r"(r1), "r"(r2)
      : RISCV_ECALL_CLOBBERS
      );
@@ -250,7 +260,7 @@ static inline uintptr_t sys_call3(unsigned int nbr, 
uintptr_t parm1,
 
   asm volatile
     (
-     "ecall"
+     RISCV_SYSCALL_INSN
      :: "r"(r0), "r"(r1), "r"(r2), "r"(r3)
      : RISCV_ECALL_CLOBBERS
      );
@@ -280,7 +290,7 @@ static inline uintptr_t sys_call4(unsigned int nbr, 
uintptr_t parm1,
 
   asm volatile
     (
-     "ecall"
+     RISCV_SYSCALL_INSN
      :: "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4)
      : RISCV_ECALL_CLOBBERS
      );
@@ -311,7 +321,7 @@ static inline uintptr_t sys_call5(unsigned int nbr, 
uintptr_t parm1,
 
   asm volatile
     (
-     "ecall"
+     RISCV_SYSCALL_INSN
      :: "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5)
      : RISCV_ECALL_CLOBBERS
      );
@@ -344,7 +354,7 @@ static inline uintptr_t sys_call6(unsigned int nbr, 
uintptr_t parm1,
 
   asm volatile
     (
-     "ecall"
+     RISCV_SYSCALL_INSN
      :: "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5), "r"(r6)
      : RISCV_ECALL_CLOBBERS
      );
diff --git a/arch/risc-v/src/ab32vg1/CMakeLists.txt 
b/arch/risc-v/src/ab32vg1/CMakeLists.txt
new file mode 100644
index 00000000000..11f13dd9c60
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/CMakeLists.txt
@@ -0,0 +1,41 @@
+# 
##############################################################################
+# arch/risc-v/src/ab32vg1/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+# license agreements.  See the NOTICE file distributed with this work for
+# additional information regarding copyright ownership.  The ASF licenses this
+# file to you under the Apache License, Version 2.0 (the "License"); you may 
not
+# use this file except in compliance with the License.  You may obtain a copy 
of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+# 
##############################################################################
+
+# Specify our HEAD assembly file.  This will be linked as the first object 
file,
+# so it will appear at address 0
+
+list(
+  APPEND
+  SRCS
+  ab32vg1_head.S
+  ab32vg1_allocateheap.c
+  ab32vg1_clockconfig.c
+  ab32vg1_gpio.c
+  ab32vg1_idle.c
+  ab32vg1_irq.c
+  ab32vg1_lowputc.c
+  ab32vg1_serial.c
+  ab32vg1_start.c
+  ab32vg1_systemreset.c
+  ab32vg1_timerisr.c)
+
+target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/risc-v/src/ab32vg1/Kconfig b/arch/risc-v/src/ab32vg1/Kconfig
new file mode 100644
index 00000000000..6f3cc51ae66
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/Kconfig
@@ -0,0 +1,39 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+comment "AB32VG1 Configuration Options"
+
+config AB32VG1_SYSCLK_MHZ
+       int "System clock frequency (MHz)"
+       default 48
+       range 24 120
+       ---help---
+               The system clock is PLL0 (240 MHz) divided by an integer, so the
+               valid values are 24, 30, 40, 48, 60, 80 and 120.  48 MHz is the
+               frequency used by the mask ROM and by the RT-Thread BSP.
+
+menu "AB32VG1 Peripheral Support"
+
+config AB32VG1_UART0
+       bool "UART0"
+       default y
+       select UART0_SERIALDRIVER
+       select ARCH_HAVE_SERIAL_TERMIOS
+       ---help---
+               UART0 in single-wire mode on PA7 (TX0 group G1, RX mapped to the
+               TX pin).  On the AB32VG1 development boards this is the pin 
wired
+               to the USB-serial bridge and used by the mask ROM UART loader.
+
+config AB32VG1_UART0_ROMKEY
+       bool "Reboot into the ROM loader on the download key"
+       default y
+       depends on AB32VG1_UART0
+       ---help---
+               Watch the UART0 receive stream for the key sent by host download
+               tools (F5 A0 A5 96 87 5A) and reset the chip when it is seen, so
+               that the mask ROM UART loader can take over without pressing the
+               reset button.
+
+endmenu
diff --git a/arch/risc-v/src/ab32vg1/Make.defs 
b/arch/risc-v/src/ab32vg1/Make.defs
new file mode 100644
index 00000000000..d9abd4c1ac5
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/Make.defs
@@ -0,0 +1,33 @@
+############################################################################
+# arch/risc-v/src/ab32vg1/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include common/Make.defs
+
+# Specify our HEAD assembly file.  This will be linked as
+# the first object file, so it will appear at address 0
+HEAD_ASRC = ab32vg1_head.S
+
+# Specify our C code within this directory to be included
+CHIP_CSRCS  = ab32vg1_allocateheap.c ab32vg1_clockconfig.c ab32vg1_gpio.c
+CHIP_CSRCS += ab32vg1_idle.c
+CHIP_CSRCS += ab32vg1_irq.c ab32vg1_lowputc.c ab32vg1_serial.c
+CHIP_CSRCS += ab32vg1_start.c ab32vg1_systemreset.c ab32vg1_timerisr.c
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1.h 
b/arch/risc-v/src/ab32vg1/ab32vg1.h
new file mode 100644
index 00000000000..e2e74ba2c38
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_AB32VG1_H
+#define __ARCH_RISCV_SRC_AB32VG1_AB32VG1_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "riscv_common_memorymap.h"
+
+#ifndef __ASSEMBLY__
+#  include <stdint.h>
+#  include "riscv_internal.h"
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The idle thread stack starts at _ebss */
+
+#ifndef __ASSEMBLY__
+#  define AB32VG1_IDLESTACK_BASE  (uintptr_t)_ebss
+#else
+#  define AB32VG1_IDLESTACK_BASE  _ebss
+#endif
+
+#define AB32VG1_IDLESTACK_TOP     (AB32VG1_IDLESTACK_BASE + SMP_STACK_SIZE)
+
+/* Symbols that the board linker script must provide, in addition to the
+ * common ones (_stext, _sbss, _ebss, ...): the end of the image in SRAM and
+ * the end of the code placed in AECRAM.
+ */
+
+#ifndef __ASSEMBLY__
+extern uint8_t _esram[];
+extern uint8_t _eaecram[];
+#endif
+
+/* System clock */
+
+#define AB32VG1_SYSCLK_FREQUENCY  (CONFIG_AB32VG1_SYSCLK_MHZ * 1000000)
+
+#ifndef __ASSEMBLY__
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_clockconfig
+ *
+ * Description:
+ *   Switch the system clock to PLL0 / n (CONFIG_AB32VG1_SYSCLK_MHZ).
+ *
+ ****************************************************************************/
+
+void ab32vg1_clockconfig(void);
+
+/****************************************************************************
+ * Name: ab32vg1_lowsetup
+ *
+ * Description:
+ *   Configure the console UART for polled output, early in the boot.
+ *
+ ****************************************************************************/
+
+void ab32vg1_lowsetup(void);
+
+/****************************************************************************
+ * Name: ab32vg1_boardinitialize
+ *
+ * Description:
+ *   Board-specific early initialization, provided by the board logic.
+ *
+ ****************************************************************************/
+
+void ab32vg1_boardinitialize(void);
+
+/****************************************************************************
+ * Name: ab32vg1_dispatch_trap
+ *
+ * Description:
+ *   Called from the trap entry code with the trap kind (0 exception,
+ *   1 interrupt, 2 system call) and the saved registers.  Returns the
+ *   register frame to restore.
+ *
+ ****************************************************************************/
+
+uintreg_t *ab32vg1_dispatch_trap(int kind, uintreg_t *regs);
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_AB32VG1_AB32VG1_H */
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_allocateheap.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_allocateheap.c
new file mode 100644
index 00000000000..aa640c985ca
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_allocateheap.c
@@ -0,0 +1,133 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_allocateheap.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <assert.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/board.h>
+#include <nuttx/kmalloc.h>
+#include <arch/board/board.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "ab32vg1.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Memory layout (see the board linker script):
+ *
+ *   SRAM    0x10000 .. _stext       free (ROM scratch during boot)
+ *   SRAM    _stext  .. _esram       image, loaded by the ROM or boot code
+ *   SRAM    _esram  .. 0x30000      free
+ *   AECRAM  0x50000 .. _eaecram     code placed in AECRAM (C library)
+ *   AECRAM  _eaecram.. 0x57a00      free
+ *   ICRAM   0x60000 .. _ebss        .bss
+ *   ICRAM   _ebss   .. idle top     idle thread stack
+ *   ICRAM   idle top.. 0x68000      free
+ *
+ * The primary heap is SRAM after the image; the other free chunks are added
+ * as extra regions up to CONFIG_MM_REGIONS.
+ */
+
+#define HEAP_ALIGN(a)   (((uintptr_t)(a) + 15) & ~(uintptr_t)15)
+
+#define SRAM_END        (AB32VG1_SRAM_BASE + AB32VG1_SRAM_SIZE)
+#define AECRAM_END      (AB32VG1_AECRAM_BASE + AB32VG1_AECRAM_SIZE)
+#define ICRAM_END       (AB32VG1_ICRAM_BASE + AB32VG1_ICRAM_SIZE)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#if CONFIG_MM_REGIONS > 1
+static void ab32vg1_addchunk(uintptr_t start, uintptr_t end)
+{
+  start = HEAP_ALIGN(start);
+  if (start + 64 < end)
+    {
+      kumm_addregion((void *)start, end - start);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_allocate_heap
+ *
+ * Description:
+ *   Return the primary heap: SRAM between the end of the image and the end
+ *   of SRAM.
+ *
+ ****************************************************************************/
+
+void up_allocate_heap(void **heap_start, size_t *heap_size)
+{
+  uintptr_t start = HEAP_ALIGN(_esram);
+
+  DEBUGASSERT(start < SRAM_END);
+
+  board_autoled_on(LED_HEAPALLOCATE);
+  *heap_start = (void *)start;
+  *heap_size  = SRAM_END - start;
+}
+
+/****************************************************************************
+ * Name: riscv_addregion
+ *
+ * Description:
+ *   RAM may be added in non-contiguous chunks.  This routine adds all chunks
+ *   that may be used for heap.
+ *
+ ****************************************************************************/
+
+#if CONFIG_MM_REGIONS > 1
+void riscv_addregion(void)
+{
+  /* ICRAM after .bss and the idle stack */
+
+  ab32vg1_addchunk(g_idle_topstack, ICRAM_END);
+
+#if CONFIG_MM_REGIONS > 2
+  /* AECRAM after the code placed there */
+
+  ab32vg1_addchunk((uintptr_t)_eaecram, AECRAM_END);
+#endif
+
+#if CONFIG_MM_REGIONS > 3
+  /* SRAM below the image */
+
+  ab32vg1_addchunk(AB32VG1_SRAM_BASE, (uintptr_t)_stext);
+#endif
+}
+#endif
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_clockconfig.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_clockconfig.c
new file mode 100644
index 00000000000..bbd9d263d8e
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_clockconfig.c
@@ -0,0 +1,111 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_clockconfig.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "ab32vg1.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* System clock = PLL0 (240 MHz) / (SPLLDIV + 1) */
+
+#define SPLL_DIV    (AB32VG1_PLL0_FREQUENCY / AB32VG1_SYSCLK_FREQUENCY - 1)
+
+#if (AB32VG1_PLL0_FREQUENCY % AB32VG1_SYSCLK_FREQUENCY) != 0
+#  error CONFIG_AB32VG1_SYSCLK_MHZ must divide 240 MHz
+#endif
+
+/* The internal SPI flash clock must stay at or below 24 MHz:
+ * SPI0BAUD = sysclk / 24 MHz, rounded up, minus 1.
+ */
+
+#define SPI0_BAUD   ((AB32VG1_SYSCLK_FREQUENCY + 23999999) / 24000000 - 1)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void ab32vg1_busywait(volatile uint32_t loops)
+{
+  while (loops-- > 0)
+    {
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_clockconfig
+ *
+ * Description:
+ *   Switch the system clock to PLL0 / n.  Follows set_sysclk() from the
+ *   RT-Thread AB32VG1 BSP (system_ab32vgx.c, Apache-2.0).  Must run before
+ *   interrupts are enabled.
+ *
+ ****************************************************************************/
+
+void ab32vg1_clockconfig(void)
+{
+  /* Start PLL0 at 240 MHz if the boot code did not */
+
+  if ((getreg32(AB32VG1_PLL0CON) & PLL0CON_LDO_EN) == 0)
+    {
+      modifyreg32(AB32VG1_PLL0CON, PLL0CON_VCO_MASK, PLL0CON_VCO_HIGH);
+      modifyreg32(AB32VG1_PLL0CON, 0, PLL0CON_LDO_EN);
+      ab32vg1_busywait(1000);                   /* >= 100 us */
+      putreg32(AB32VG1_PLL0DIV_240M, AB32VG1_PLL0DIV);
+      modifyreg32(AB32VG1_PLL0CON, 0, PLL0CON_DIV_UPDATE);
+      modifyreg32(AB32VG1_PLL0CON, 0, PLL0CON_ANALOG_EN);
+      modifyreg32(AB32VG1_PLL0CON, 0, PLL0CON_SDM_EN);
+      ab32vg1_busywait(10000);                  /* >= 1 ms to lock */
+    }
+
+  /* Run from the 2 MHz RC oscillator while the dividers change */
+
+  modifyreg32(AB32VG1_CLKCON0, CLKCON0_SYSCLK_MASK, CLKCON0_SYSCLK_RC2M);
+  modifyreg32(AB32VG1_CLKCON2, CLKCON2_SPLLDIV_MASK, 0);
+  modifyreg32(AB32VG1_CLKCON0, CLKCON0_SPLL_MASK, CLKCON0_SPLL_PLL0);
+
+  if (getreg32(AB32VG1_PLL0DIV) != AB32VG1_PLL0DIV_240M)
+    {
+      putreg32(AB32VG1_PLL0DIV_240M, AB32VG1_PLL0DIV);
+      modifyreg32(AB32VG1_PLL0CON, PLL0CON_VCO_MASK, PLL0CON_VCO_HIGH);
+      modifyreg32(AB32VG1_PLL0CON, 0, PLL0CON_DIV_UPDATE);
+    }
+
+  modifyreg32(AB32VG1_CLKCON2, CLKCON2_SPLLDIV_MASK,
+              SPLL_DIV << CLKCON2_SPLLDIV_SHIFT);
+  putreg32(SPI0_BAUD, AB32VG1_SPI0BAUD);
+  modifyreg32(AB32VG1_CLKCON0, CLKCON0_SYSCLK_MASK, CLKCON0_SYSCLK_SPLL);
+}
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_gpio.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_gpio.c
new file mode 100644
index 00000000000..4a03cdc680a
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_gpio.c
@@ -0,0 +1,155 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_gpio.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <nuttx/irq.h>
+#include <arch/irq.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "ab32vg1_gpio.h"
+#include "hardware/ab32vg1_gpio.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const uintptr_t g_portbase[] =
+{
+  AB32VG1_GPIOA_BASE,
+  AB32VG1_GPIOB_BASE,
+  AB32VG1_GPIOE_BASE,
+  AB32VG1_GPIOF_BASE,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline uintptr_t ab32vg1_portbase(gpio_pinset_t pinset)
+{
+  return g_portbase[(pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT];
+}
+
+static inline uint32_t ab32vg1_pinmask(gpio_pinset_t pinset)
+{
+  return 1 << ((pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_configgpio
+ ****************************************************************************/
+
+int ab32vg1_configgpio(gpio_pinset_t cfgset)
+{
+  uintptr_t base;
+  uint32_t mask;
+  irqstate_t flags;
+
+  if (((cfgset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT) >= 4)
+    {
+      return -EINVAL;
+    }
+
+  base = ab32vg1_portbase(cfgset);
+  mask = ab32vg1_pinmask(cfgset);
+
+  flags = up_irq_save();
+
+  /* Digital GPIO function */
+
+  modifyreg32(base + AB32VG1_GPIO_FEN_OFFSET, mask, 0);
+  modifyreg32(base + AB32VG1_GPIO_DE_OFFSET, 0, mask);
+
+  /* Pull resistors */
+
+  modifyreg32(base + AB32VG1_GPIO_PU_OFFSET, mask, 0);
+  modifyreg32(base + AB32VG1_GPIO_PD_OFFSET, mask, 0);
+  modifyreg32(base + AB32VG1_GPIO_PU200K_OFFSET, mask, 0);
+  modifyreg32(base + AB32VG1_GPIO_PD200K_OFFSET, mask, 0);
+  modifyreg32(base + AB32VG1_GPIO_PU300_OFFSET, mask, 0);
+  modifyreg32(base + AB32VG1_GPIO_PD300_OFFSET, mask, 0);
+
+  switch (cfgset & GPIO_PULL_MASK)
+    {
+      case GPIO_PULLUP:
+        modifyreg32(base + AB32VG1_GPIO_PU_OFFSET, 0, mask);
+        break;
+
+      case GPIO_PULLDOWN:
+        modifyreg32(base + AB32VG1_GPIO_PD_OFFSET, 0, mask);
+        break;
+
+      default:
+        break;
+    }
+
+  /* Direction; set the output level before driving the pin */
+
+  if (cfgset & GPIO_OUTPUT)
+    {
+      putreg32(mask, base + ((cfgset & GPIO_VALUE_ONE) ?
+                             AB32VG1_GPIO_SET_OFFSET :
+                             AB32VG1_GPIO_CLR_OFFSET));
+      modifyreg32(base + AB32VG1_GPIO_DIR_OFFSET, mask, 0);
+    }
+  else
+    {
+      modifyreg32(base + AB32VG1_GPIO_DIR_OFFSET, 0, mask);
+    }
+
+  up_irq_restore(flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ab32vg1_gpiowrite
+ ****************************************************************************/
+
+void ab32vg1_gpiowrite(gpio_pinset_t pinset, bool value)
+{
+  putreg32(ab32vg1_pinmask(pinset), ab32vg1_portbase(pinset) +
+           (value ? AB32VG1_GPIO_SET_OFFSET : AB32VG1_GPIO_CLR_OFFSET));
+}
+
+/****************************************************************************
+ * Name: ab32vg1_gpioread
+ ****************************************************************************/
+
+bool ab32vg1_gpioread(gpio_pinset_t pinset)
+{
+  return (getreg32(ab32vg1_portbase(pinset) + AB32VG1_GPIO_DATA_OFFSET) &
+          ab32vg1_pinmask(pinset)) != 0;
+}
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_gpio.h 
b/arch/risc-v/src/ab32vg1/ab32vg1_gpio.h
new file mode 100644
index 00000000000..fe7c500929a
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_gpio.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_gpio.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_AB32VG1_GPIO_H
+#define __ARCH_RISCV_SRC_AB32VG1_AB32VG1_GPIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Pin configuration encoding (16 bits):
+ *
+ *   1111 1100 0000 0000
+ *   5432 1098 7654 3210
+ *   ---- ---- ---- ----
+ *   .... .... .... .PPP  Pin number 0..7
+ *   .... .... ..PP P...  Port: A, B, E, F
+ *   .... .... .D.. ....  Direction: 1 = output
+ *   .... .... V... ....  Initial output value
+ *   .... ..UU .... ....  Pull: none, up, down (10 Kohm)
+ */
+
+#define GPIO_PIN_SHIFT      0
+#define GPIO_PIN_MASK       (7 << GPIO_PIN_SHIFT)
+#  define GPIO_PIN(n)       ((n) << GPIO_PIN_SHIFT)
+
+#define GPIO_PORT_SHIFT     3
+#define GPIO_PORT_MASK      (7 << GPIO_PORT_SHIFT)
+#  define GPIO_PORTA        (0 << GPIO_PORT_SHIFT)
+#  define GPIO_PORTB        (1 << GPIO_PORT_SHIFT)
+#  define GPIO_PORTE        (2 << GPIO_PORT_SHIFT)
+#  define GPIO_PORTF        (3 << GPIO_PORT_SHIFT)
+
+#define GPIO_OUTPUT         (1 << 6)
+#define GPIO_INPUT          (0)
+#define GPIO_VALUE_ONE      (1 << 7)
+#define GPIO_VALUE_ZERO     (0)
+
+#define GPIO_PULL_SHIFT     8
+#define GPIO_PULL_MASK      (3 << GPIO_PULL_SHIFT)
+#  define GPIO_FLOAT        (0 << GPIO_PULL_SHIFT)
+#  define GPIO_PULLUP       (1 << GPIO_PULL_SHIFT)
+#  define GPIO_PULLDOWN     (2 << GPIO_PULL_SHIFT)
+
+#ifndef __ASSEMBLY__
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+typedef uint16_t gpio_pinset_t;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_configgpio
+ *
+ * Description:
+ *   Configure a pin as a digital GPIO according to the encoded pinset.
+ *
+ ****************************************************************************/
+
+int ab32vg1_configgpio(gpio_pinset_t cfgset);
+
+/****************************************************************************
+ * Name: ab32vg1_gpiowrite
+ *
+ * Description:
+ *   Drive an output pin.
+ *
+ ****************************************************************************/
+
+void ab32vg1_gpiowrite(gpio_pinset_t pinset, bool value);
+
+/****************************************************************************
+ * Name: ab32vg1_gpioread
+ *
+ * Description:
+ *   Read the level of a pin.
+ *
+ ****************************************************************************/
+
+bool ab32vg1_gpioread(gpio_pinset_t pinset);
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_RISCV_SRC_AB32VG1_AB32VG1_GPIO_H */
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_head.S 
b/arch/risc-v/src/ab32vg1/ab32vg1_head.S
new file mode 100644
index 00000000000..385657289d6
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_head.S
@@ -0,0 +1,297 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_head.S
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <arch/irq.h>
+#include <arch/mode.h>
+
+#include "chip.h"
+#include "ab32vg1.h"
+#include "riscv_macros.S"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Trap kinds passed to ab32vg1_dispatch_trap() */
+
+#define TRAP_EXCEPTION      0
+#define TRAP_IRQ            1
+#define TRAP_SYSCALL        2
+
+/* Status word stored in REG_INT_CTX: STATUS_PIE mirrors EPICCON.IE, so the
+ * common code (signal delivery, swint) can keep using the standard bits.
+ */
+
+#define INTCTX_PIE_SHIFT    7               /* log2(STATUS_PIE) */
+
+/****************************************************************************
+ * Public Symbols
+ ****************************************************************************/
+
+  .global __start
+  .global ab32vg1_vectors
+  .global ab32vg1_syscall_trap
+  .global ab32vg1_trap_return
+  .global up_saveusercontext
+
+/****************************************************************************
+ * Name: __start
+ *
+ * Description:
+ *   Image entry point.  The mask ROM UART loader calls the image at its
+ *   load address as a command handler, from the ROM stack at the top of
+ *   SRAM; a boot header loader jumps to it.  Never returns.
+ *
+ ****************************************************************************/
+
+  .section .start, "ax"
+  .type   __start, function
+
+__start:
+
+  /* Mask all interrupts: the ROM leaves its HSUART interrupt enabled */
+
+  sw      zero, AB32VG1_PICEN(zero)
+  li      t0, (PICCON_IE | PICCON_EN)
+  sw      t0, AB32VG1_PICCONCLR(zero)
+
+  /* Install our vector table */
+
+  la      t0, ab32vg1_vectors
+  sw      t0, AB32VG1_PICADR(zero)
+
+  /* Set the stack pointer to the idle thread stack */
+
+  la      sp, AB32VG1_IDLESTACK_TOP
+
+  /* Jump to __ab32vg1_start; it never returns */
+
+  jal     x1, __ab32vg1_start
+
+1:
+  j       1b
+
+  .size   __start, . - __start
+
+/****************************************************************************
+ * Name: ab32vg1_vectors
+ *
+ * Description:
+ *   Vector table pointed by PICADR.  Entries are fixed offsets from the
+ *   base: +0x10 exceptions (and high priority interrupts, unused), +0x40
+ *   low priority interrupts, +0x80 XIP cache miss (unused, the image runs
+ *   from RAM).
+ *
+ ****************************************************************************/
+
+  .section .text.ab32vg1_vectors, "ax"
+  .balign 1024
+  .type   ab32vg1_vectors, function
+
+ab32vg1_vectors:
+  j       exception_entry               /* +0x00: not expected */
+  .org    ab32vg1_vectors + AB32VG1_VEC_EXCEPTION
+  j       exception_entry
+  .org    ab32vg1_vectors + AB32VG1_VEC_IRQ
+  j       irq_entry
+  .org    ab32vg1_vectors + AB32VG1_VEC_CACHE
+  j       exception_entry
+
+  .size   ab32vg1_vectors, . - ab32vg1_vectors
+
+/****************************************************************************
+ * Name: trap_entry
+ *
+ * Description:
+ *   Save the full context on the current stack and call
+ *   ab32vg1_dispatch_trap(kind, regs).  Used by the hardware vectors.
+ *
+ ****************************************************************************/
+
+.macro trap_entry kind
+
+  addi    sp, sp, -XCPTCONTEXT_SIZE
+  save_ctx sp
+
+  lw      s0, AB32VG1_EPICCON(zero)     /* Previous interrupt enable */
+  andi    s0, s0, PICCON_IE
+  slli    s0, s0, INTCTX_PIE_SHIFT
+  li      s1, STATUS_PPP
+  or      s0, s0, s1
+  REGSTORE s0, REG_INT_CTX(sp)
+
+  lw      s1, AB32VG1_EPC(zero)         /* Return address */
+  REGSTORE s1, REG_EPC(sp)
+
+  addi    s2, sp, XCPTCONTEXT_SIZE      /* Interrupted sp */
+  REGSTORE s2, REG_SP(sp)
+
+  li      a0, \kind
+  j       trap_dispatch
+
+.endm
+
+  .section .text.ab32vg1_trap, "ax"
+
+exception_entry:
+  trap_entry TRAP_EXCEPTION
+
+irq_entry:
+  trap_entry TRAP_IRQ
+
+/****************************************************************************
+ * Name: ab32vg1_syscall_trap
+ *
+ * Description:
+ *   Software equivalent of ecall, reached through RISCV_SYSCALL_INSN with
+ *   "jal ra, ab32vg1_syscall_trap" (the caller preserves its own ra).
+ *   Builds the same frame as a hardware trap with interrupts masked.  The
+ *   saved EPC is ra - 4 because riscv_doirq() advances it by 4, as it does
+ *   for a real ecall.
+ *
+ ****************************************************************************/
+
+  .type   ab32vg1_syscall_trap, function
+
+ab32vg1_syscall_trap:
+
+  addi    sp, sp, -XCPTCONTEXT_SIZE
+  save_ctx sp
+
+  lw      s0, AB32VG1_PICCON(zero)      /* Current interrupt enable */
+  li      s1, PICCON_IE
+  sw      s1, AB32VG1_PICCONCLR(zero)   /* Mask interrupts */
+  andi    s0, s0, PICCON_IE
+  slli    s0, s0, INTCTX_PIE_SHIFT
+  li      s1, STATUS_PPP
+  or      s0, s0, s1
+  REGSTORE s0, REG_INT_CTX(sp)
+
+  addi    s1, ra, -4
+  REGSTORE s1, REG_EPC(sp)
+
+  addi    s2, sp, XCPTCONTEXT_SIZE
+  REGSTORE s2, REG_SP(sp)
+
+  li      a0, TRAP_SYSCALL
+
+  /* Fall through */
+
+/****************************************************************************
+ * Name: trap_dispatch
+ *
+ * Description:
+ *   a0 = trap kind, sp = register frame.  Switch to the interrupt stack,
+ *   call the C dispatcher and return to the context it selects.
+ *
+ ****************************************************************************/
+
+trap_dispatch:
+
+  mv      a1, sp                        /* a1 = regs */
+
+#if CONFIG_ARCH_INTERRUPTSTACK > 15
+  setintstack t0, t1
+#endif
+
+  call    ab32vg1_dispatch_trap
+
+  /* Fall through with a0 = the register frame to restore */
+
+/****************************************************************************
+ * Name: ab32vg1_trap_return
+ *
+ * Description:
+ *   Restore the context in a0 and return to it.  EPC and EPICCON are
+ *   written before mret; interrupts are masked here, either by hardware
+ *   (inside a trap) or by ab32vg1_syscall_trap.
+ *
+ ****************************************************************************/
+
+ab32vg1_trap_return:
+
+  mv      sp, a0
+
+  REGLOAD s0, REG_EPC(sp)
+  sw      s0, AB32VG1_EPC(zero)
+
+  REGLOAD s0, REG_INT_CTX(sp)
+  srli    s0, s0, INTCTX_PIE_SHIFT
+  andi    s0, s0, PICCON_IE
+  sw      s0, AB32VG1_EPICCON(zero)
+
+  load_ctx sp
+  REGLOAD sp, REG_SP(sp)
+
+  mret
+
+  .size   ab32vg1_syscall_trap, . - ab32vg1_syscall_trap
+
+/****************************************************************************
+ * Name: up_saveusercontext
+ *
+ * Description:
+ *   Save the current register context (used by the assertion logic).
+ *
+ ****************************************************************************/
+
+  .type   up_saveusercontext, function
+
+up_saveusercontext:
+
+  save_ctx a0
+
+  lw      a1, AB32VG1_PICCON(zero)
+  andi    a1, a1, PICCON_IE
+  slli    a1, a1, INTCTX_PIE_SHIFT
+  REGSTORE a1, REG_INT_CTX(a0)
+
+  REGSTORE sp, REG_X2(a0)
+  REGSTORE x1, REG_EPC(a0)
+
+  li      a0, 0
+  ret
+
+  .size   up_saveusercontext, . - up_saveusercontext
+
+/****************************************************************************
+ * Name: g_intstackalloc and g_intstacktop
+ ****************************************************************************/
+
+#if CONFIG_ARCH_INTERRUPTSTACK > 15
+  .bss
+  .balign 16
+  .global g_intstackalloc
+  .global g_intstacktop
+  .type   g_intstackalloc, object
+  .type   g_intstacktop, object
+g_intstackalloc:
+  .skip   STACKFRAME_ALIGN_UP(INT_STACK_SIZE)
+g_intstacktop:
+  .size   g_intstacktop, 0
+  .size   g_intstackalloc, STACKFRAME_ALIGN_DOWN(INT_STACK_SIZE)
+#endif
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_idle.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_idle.c
new file mode 100644
index 00000000000..41ac0002d5a
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_idle.c
@@ -0,0 +1,61 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_idle.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/board.h>
+#include <arch/board/board.h>
+
+#include "riscv_internal.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_idle
+ *
+ * Description:
+ *   up_idle() is the logic that will be executed when there is no other
+ *   ready-to-run task.  This is processor idle time and will continue until
+ *   some interrupt occurs to cause a context switch from the idle task.
+ *
+ *   The AB32VG1 core has no standard wfi (system instructions trap and the
+ *   core cannot recover from that), so the idle loop simply spins.
+ *
+ ****************************************************************************/
+
+void up_idle(void)
+{
+#if defined(CONFIG_SUPPRESS_INTERRUPTS) || defined(CONFIG_SUPPRESS_TIMER_INTS)
+  /* If the system is idle and there are no timer interrupts, then process
+   * "fake" timer interrupts. Hopefully, something will wake up.
+   */
+
+  nxsched_process_timer();
+#endif
+}
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_irq.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_irq.c
new file mode 100644
index 00000000000..db080f94817
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_irq.c
@@ -0,0 +1,226 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_irq.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/board.h>
+#include <arch/irq.h>
+#include <arch/board/board.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "ab32vg1.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define TRAP_EXCEPTION  0
+#define TRAP_IRQ        1
+#define TRAP_SYSCALL    2
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_exception
+ *
+ * Description:
+ *   Fatal trap: illegal instruction, CSR access, ecall, ebreak or a
+ *   spurious XIP cache miss.  The core does not report the cause and
+ *   cannot resume after an exception (it keeps trapping on every
+ *   instruction), so this never returns.
+ *
+ ****************************************************************************/
+
+static void ab32vg1_exception(uintreg_t *regs)
+{
+  _alert("EXCEPTION: EPC: %08" PRIxREG " (address after the faulting "
+         "instruction) CPU status: %08" PRIx32 "\n",
+         regs[REG_EPC], getreg32(AB32VG1_CPU_STATUS));
+
+  up_set_interrupt_context(true);
+  PANIC_WITH_REGS("panic", regs);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_dispatch_trap
+ ****************************************************************************/
+
+uintreg_t *ab32vg1_dispatch_trap(int kind, uintreg_t *regs)
+{
+  uint32_t pending;
+  int bit;
+
+  switch (kind)
+    {
+      case TRAP_SYSCALL:
+        return riscv_doirq(RISCV_IRQ_ECALLM, regs);
+
+      case TRAP_IRQ:
+
+        /* Dispatch one source per trap: riscv_doirq() may switch context.
+         * Remaining sources trap again right after mret.
+         */
+
+        pending = getreg32(AB32VG1_PICPND) & getreg32(AB32VG1_PICEN);
+        if (pending == 0)
+          {
+            return regs;
+          }
+
+        bit = __builtin_ctz(pending);
+        if (bit == AB32VG1_IRQ_SW - AB32VG1_IRQ_FIRST)
+          {
+            putreg32(1 << bit, AB32VG1_PICPND);
+          }
+
+        return riscv_doirq(AB32VG1_IRQ_FIRST + bit, regs);
+
+      default:
+        ab32vg1_exception(regs);
+        return regs;
+    }
+}
+
+/****************************************************************************
+ * Name: up_irqinitialize
+ ****************************************************************************/
+
+void up_irqinitialize(void)
+{
+  /* Disable all sources, all low priority, unit enabled, IE off */
+
+  up_irq_save();
+  putreg32(0, AB32VG1_PICEN);
+  putreg32(0, AB32VG1_PICPR);
+  putreg32(0xffffffff, AB32VG1_PICPND);
+  modifyreg32(AB32VG1_PICCON, PICCON_IE, PICCON_INIT);
+
+  /* Colorize the interrupt stack for debug purposes */
+
+  riscv_color_intstack();
+
+  /* The software trap is dispatched as an M-mode ecall */
+
+  irq_attach(RISCV_IRQ_ECALLM, riscv_swint, NULL);
+
+#ifndef CONFIG_SUPPRESS_INTERRUPTS
+  /* And finally, enable interrupts */
+
+  up_irq_enable();
+#endif
+}
+
+/****************************************************************************
+ * Name: up_disable_irq
+ *
+ * Description:
+ *   Disable the IRQ specified by 'irq'
+ *
+ ****************************************************************************/
+
+void up_disable_irq(int irq)
+{
+  irqstate_t flags;
+
+  if (irq >= AB32VG1_IRQ_FIRST && irq < NR_IRQS)
+    {
+      flags = up_irq_save();
+      modifyreg32(AB32VG1_PICEN, 1 << (irq - AB32VG1_IRQ_FIRST), 0);
+      up_irq_restore(flags);
+    }
+}
+
+/****************************************************************************
+ * Name: up_enable_irq
+ *
+ * Description:
+ *   Enable the IRQ specified by 'irq'
+ *
+ ****************************************************************************/
+
+void up_enable_irq(int irq)
+{
+  irqstate_t flags;
+
+  if (irq >= AB32VG1_IRQ_FIRST && irq < NR_IRQS)
+    {
+      flags = up_irq_save();
+      modifyreg32(AB32VG1_PICEN, 0, 1 << (irq - AB32VG1_IRQ_FIRST));
+      up_irq_restore(flags);
+    }
+}
+
+/****************************************************************************
+ * Name: up_irq_enable
+ *
+ * Description:
+ *   Return the current interrupt state and enable interrupts
+ *
+ ****************************************************************************/
+
+irqstate_t up_irq_enable(void)
+{
+  irqstate_t flags = getreg32(AB32VG1_PICCON) & PICCON_IE;
+
+  putreg32(PICCON_IE, AB32VG1_PICCONSET);
+  return flags;
+}
+
+/****************************************************************************
+ * Name: riscv_get_newintctx
+ *
+ * Description:
+ *   Return the initial REG_INT_CTX of a new thread: interrupts enabled
+ *   once the thread starts (STATUS_PIE is copied to EPICCON.IE on return).
+ *
+ ****************************************************************************/
+
+uintptr_t riscv_get_newintctx(void)
+{
+  return STATUS_PPP | STATUS_PIE;
+}
+
+/****************************************************************************
+ * Name: riscv_set_idleintctx
+ ****************************************************************************/
+
+void riscv_set_idleintctx(void)
+{
+}
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_lowputc.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_lowputc.c
new file mode 100644
index 00000000000..30c5300039b
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_lowputc.c
@@ -0,0 +1,142 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_lowputc.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <arch/board/board.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "ab32vg1.h"
+#include "ab32vg1_lowputc.h"
+#include "hardware/ab32vg1_uart.h"
+#include "hardware/ab32vg1_gpio.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_UART0_SERIAL_CONSOLE
+#  define HAVE_CONSOLE  1
+#endif
+
+#define UART0_CON       (AB32VG1_UART0_BASE + AB32VG1_UART_CON_OFFSET)
+#define UART0_CPND      (AB32VG1_UART0_BASE + AB32VG1_UART_CPND_OFFSET)
+#define UART0_BAUD      (AB32VG1_UART0_BASE + AB32VG1_UART_BAUD_OFFSET)
+#define UART0_DATA      (AB32VG1_UART0_BASE + AB32VG1_UART_DATA_OFFSET)
+
+/* PA7 is the UART0 TX pin of pin group G1 */
+
+#define UART0_PIN       (1 << 7)
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_uart0_configure
+ ****************************************************************************/
+
+void ab32vg1_uart0_configure(uint32_t baud, bool stop2)
+{
+  uint32_t div = (UART_INC_FREQUENCY + baud / 2) / baud - 1;
+  uint32_t con;
+
+  putreg32(0, UART0_CON);
+
+  /* Clocks: uart_inc and the UART0 gate */
+
+  modifyreg32(AB32VG1_CLKCON1, 0, CLKCON1_UART_INC);
+  modifyreg32(AB32VG1_CLKGAT0, 0, CLKGAT0_UART0);
+
+  /* PA7: digital input with pull-up, driven by the peripheral */
+
+  modifyreg32(AB32VG1_GPIOA_BASE + AB32VG1_GPIO_FEN_OFFSET, 0, UART0_PIN);
+  modifyreg32(AB32VG1_GPIOA_BASE + AB32VG1_GPIO_DE_OFFSET, 0, UART0_PIN);
+  modifyreg32(AB32VG1_GPIOA_BASE + AB32VG1_GPIO_DIR_OFFSET, 0, UART0_PIN);
+  modifyreg32(AB32VG1_GPIOA_BASE + AB32VG1_GPIO_PD_OFFSET, UART0_PIN, 0);
+  modifyreg32(AB32VG1_GPIOA_BASE + AB32VG1_GPIO_PU_OFFSET, 0, UART0_PIN);
+
+  /* TX0 on group G1 (PA7), RX0 on the TX pin */
+
+  putreg32((FUNCMCON_CLEAR << FUNCMCON0_UT0RXMAP_SHIFT) |
+           (FUNCMCON_CLEAR << FUNCMCON0_UT0TXMAP_SHIFT),
+           AB32VG1_FUNCMCON0);
+  putreg32((FUNCMCON_RX_ON_TX << FUNCMCON0_UT0RXMAP_SHIFT) |
+           (FUNCMCON_G(1) << FUNCMCON0_UT0TXMAP_SHIFT),
+           AB32VG1_FUNCMCON0);
+
+  putreg32(UART_BAUD(div), UART0_BAUD);
+
+  con = UART_CON_RXEN | UART_CON_ONELINE | UART_CON_CLKSRC | UART_CON_UTEN;
+  if (stop2)
+    {
+      con |= UART_CON_SB2EN;
+    }
+
+  putreg32(UART_CPND_CRXPND | UART_CPND_CTXPND, UART0_CPND);
+  putreg32(con, UART0_CON);
+}
+
+/****************************************************************************
+ * Name: riscv_lowputc
+ *
+ * Description:
+ *   Output one byte on the serial console.  TXPND is set when a byte has
+ *   been shifted out and cleared by writing the data register; it reads 0
+ *   before the first byte, so wait after writing rather than before.
+ *
+ ****************************************************************************/
+
+void riscv_lowputc(char ch)
+{
+#ifdef HAVE_CONSOLE
+  putreg32((uint8_t)ch, UART0_DATA);
+  while ((getreg32(UART0_CON) & UART_CON_TXPND) == 0)
+    {
+    }
+#endif
+}
+
+/****************************************************************************
+ * Name: ab32vg1_lowsetup
+ *
+ * Description:
+ *   This performs basic initialization of the UART used for the serial
+ *   console.  Its purpose is to get the console output available as soon
+ *   as possible.
+ *
+ ****************************************************************************/
+
+void ab32vg1_lowsetup(void)
+{
+#ifdef HAVE_CONSOLE
+  ab32vg1_uart0_configure(CONFIG_UART0_BAUD, CONFIG_UART0_2STOP != 0);
+#endif
+}
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_lowputc.h 
b/arch/risc-v/src/ab32vg1/ab32vg1_lowputc.h
new file mode 100644
index 00000000000..424672c0e15
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_lowputc.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_lowputc.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_AB32VG1_LOWPUTC_H
+#define __ARCH_RISCV_SRC_AB32VG1_AB32VG1_LOWPUTC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_uart0_configure
+ *
+ * Description:
+ *   Configure UART0 in single-wire mode on PA7: pin mux, clocks, baud
+ *   rate (clocked from uart_inc = 13 MHz) and frame format.  Interrupts
+ *   are left disabled.
+ *
+ ****************************************************************************/
+
+void ab32vg1_uart0_configure(uint32_t baud, bool stop2);
+
+#endif /* __ARCH_RISCV_SRC_AB32VG1_AB32VG1_LOWPUTC_H */
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_serial.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_serial.c
new file mode 100644
index 00000000000..6931b4896f2
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_serial.c
@@ -0,0 +1,631 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_serial.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#ifdef CONFIG_SERIAL_TERMIOS
+#  include <termios.h>
+#endif
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/serial/serial.h>
+
+#include <arch/board/board.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "ab32vg1.h"
+#include "ab32vg1_lowputc.h"
+#include "hardware/ab32vg1_uart.h"
+
+#ifdef USE_SERIALDRIVER
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef CONFIG_AB32VG1_UART0
+#  error "The serial driver requires CONFIG_AB32VG1_UART0"
+#endif
+
+#ifdef CONFIG_UART0_SERIAL_CONSOLE
+#  define CONSOLE_DEV     g_uart0port   /* UART0 is console */
+#  define HAVE_CONSOLE    1
+#endif
+
+#define TTYS0_DEV         g_uart0port   /* UART0 is ttyS0 */
+
+/* Key sent by host tools to reach the mask ROM loader: the "reset" token
+ * followed by the sync token (see scripts/ab32vg1_dl.py).
+ */
+
+#define ROMKEY_LEN        6
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ab32vg1_uart_s
+{
+  uintptr_t base;         /* Base address of the UART registers */
+  uint32_t  baud;         /* Configured baud */
+  int       irq;          /* PIC interrupt (shared by UART0/1/2) */
+  bool      stop2;        /* Two stop bits */
+  bool      txbusy;       /* A byte was written and TXPND is pending */
+#ifdef CONFIG_AB32VG1_UART0_ROMKEY
+  uint8_t   keymatch;     /* Number of key bytes matched so far */
+#endif
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int  ab32vg1_setup(struct uart_dev_s *dev);
+static void ab32vg1_shutdown(struct uart_dev_s *dev);
+static int  ab32vg1_attach(struct uart_dev_s *dev);
+static void ab32vg1_detach(struct uart_dev_s *dev);
+static int  ab32vg1_interrupt(int irq, void *context, void *arg);
+static int  ab32vg1_ioctl(struct file *filep, int cmd, unsigned long arg);
+static int  ab32vg1_receive(struct uart_dev_s *dev, unsigned int *status);
+static void ab32vg1_rxint(struct uart_dev_s *dev, bool enable);
+static bool ab32vg1_rxavailable(struct uart_dev_s *dev);
+static void ab32vg1_send(struct uart_dev_s *dev, int ch);
+static void ab32vg1_txint(struct uart_dev_s *dev, bool enable);
+static bool ab32vg1_txready(struct uart_dev_s *dev);
+static bool ab32vg1_txempty(struct uart_dev_s *dev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct uart_ops_s g_uart_ops =
+{
+  .setup          = ab32vg1_setup,
+  .shutdown       = ab32vg1_shutdown,
+  .attach         = ab32vg1_attach,
+  .detach         = ab32vg1_detach,
+  .ioctl          = ab32vg1_ioctl,
+  .receive        = ab32vg1_receive,
+  .rxint          = ab32vg1_rxint,
+  .rxavailable    = ab32vg1_rxavailable,
+#ifdef CONFIG_SERIAL_IFLOWCONTROL
+  .rxflowcontrol  = NULL,
+#endif
+  .send           = ab32vg1_send,
+  .txint          = ab32vg1_txint,
+  .txready        = ab32vg1_txready,
+  .txempty        = ab32vg1_txempty,
+};
+
+#ifdef CONFIG_AB32VG1_UART0_ROMKEY
+static const uint8_t g_romkey[ROMKEY_LEN] =
+{
+  0xf5, 0xa0, 0xa5, 0x96, 0x87, 0x5a
+};
+#endif
+
+static char g_uart0rxbuffer[CONFIG_UART0_RXBUFSIZE];
+static char g_uart0txbuffer[CONFIG_UART0_TXBUFSIZE];
+
+static struct ab32vg1_uart_s g_uart0priv =
+{
+  .base   = AB32VG1_UART0_BASE,
+  .baud   = CONFIG_UART0_BAUD,
+  .irq    = AB32VG1_IRQ_UART,
+  .stop2  = CONFIG_UART0_2STOP != 0,
+};
+
+static uart_dev_t g_uart0port =
+{
+#ifdef CONFIG_UART0_SERIAL_CONSOLE
+  .isconsole = true,
+#endif
+  .recv      =
+  {
+    .size    = CONFIG_UART0_RXBUFSIZE,
+    .buffer  = g_uart0rxbuffer,
+  },
+  .xmit      =
+  {
+    .size    = CONFIG_UART0_TXBUFSIZE,
+    .buffer  = g_uart0txbuffer,
+  },
+  .ops       = &g_uart_ops,
+  .priv      = &g_uart0priv,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline uint32_t ab32vg1_serialin(struct ab32vg1_uart_s *priv,
+                                        int offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+static inline void ab32vg1_serialout(struct ab32vg1_uart_s *priv,
+                                     int offset, uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+static inline void ab32vg1_modifycon(struct ab32vg1_uart_s *priv,
+                                     uint32_t clrbits, uint32_t setbits)
+{
+  irqstate_t flags = up_irq_save();
+
+  modifyreg32(priv->base + AB32VG1_UART_CON_OFFSET, clrbits, setbits);
+  up_irq_restore(flags);
+}
+
+/****************************************************************************
+ * Name: ab32vg1_setup
+ *
+ * Description:
+ *   Configure the UART baud, bits, parity, etc.  This method is called the
+ *   first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int ab32vg1_setup(struct uart_dev_s *dev)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_UART_CONFIG
+  ab32vg1_uart0_configure(priv->baud, priv->stop2);
+  priv->txbusy = false;
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ab32vg1_shutdown
+ *
+ * Description:
+ *   Disable the UART interrupts.  This method is called when the serial
+ *   port is closed.  The console keeps working for polled output.
+ *
+ ****************************************************************************/
+
+static void ab32vg1_shutdown(struct uart_dev_s *dev)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+
+  ab32vg1_modifycon(priv, UART_CON_RXIE | UART_CON_TXIE, 0);
+}
+
+/****************************************************************************
+ * Name: ab32vg1_attach
+ *
+ * Description:
+ *   Configure the UART to operate in interrupt driven mode.  This method is
+ *   called when the serial port is opened.
+ *
+ ****************************************************************************/
+
+static int ab32vg1_attach(struct uart_dev_s *dev)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+  int ret;
+
+  ret = irq_attach(priv->irq, ab32vg1_interrupt, dev);
+  if (ret == OK)
+    {
+      up_enable_irq(priv->irq);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: ab32vg1_detach
+ *
+ * Description:
+ *   Detach UART interrupts.  This method is called when the serial port is
+ *   closed normally just before the shutdown method is called.
+ *
+ ****************************************************************************/
+
+static void ab32vg1_detach(struct uart_dev_s *dev)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+
+  up_disable_irq(priv->irq);
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: ab32vg1_interrupt
+ *
+ * Description:
+ *   UART interrupt handler.  The PIC source is shared by UART0/1/2 and is
+ *   level triggered on RXPND/TXPND of every UART with the interrupt
+ *   enabled.
+ *
+ ****************************************************************************/
+
+static int ab32vg1_interrupt(int irq, void *context, void *arg)
+{
+  struct uart_dev_s *dev = (struct uart_dev_s *)arg;
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+  uint32_t con;
+  int passes;
+
+  for (passes = 0; passes < 256; passes++)
+    {
+      con = ab32vg1_serialin(priv, AB32VG1_UART_CON_OFFSET);
+
+      if ((con & (UART_CON_RXIE | UART_CON_RXPND)) ==
+          (UART_CON_RXIE | UART_CON_RXPND))
+        {
+          uart_recvchars(dev);
+        }
+      else if ((con & (UART_CON_TXIE | UART_CON_TXPND)) ==
+               (UART_CON_TXIE | UART_CON_TXPND))
+        {
+          /* uart_xmitchars() either writes a new byte, which clears
+           * TXPND, or disables the TX interrupt.
+           */
+
+          priv->txbusy = false;
+          uart_xmitchars(dev);
+        }
+      else
+        {
+          break;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ab32vg1_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int ab32vg1_ioctl(struct file *filep, int cmd, unsigned long arg)
+{
+#ifdef CONFIG_SERIAL_TERMIOS
+  struct inode *inode = filep->f_inode;
+  struct uart_dev_s *dev = inode->i_private;
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+  struct termios *termiosp = (struct termios *)arg;
+  int ret = OK;
+
+  switch (cmd)
+    {
+      case TCGETS:
+        if (termiosp == NULL)
+          {
+            return -EINVAL;
+          }
+
+        termiosp->c_cflag = CS8 | (priv->stop2 ? CSTOPB : 0);
+        cfsetispeed(termiosp, priv->baud);
+        break;
+
+      case TCSETS:
+        if (termiosp == NULL)
+          {
+            return -EINVAL;
+          }
+
+        /* Only 8N1/8N2 framing is supported */
+
+        if ((termiosp->c_cflag & (CSIZE | PARENB)) != CS8)
+          {
+            return -EINVAL;
+          }
+
+        priv->stop2 = (termiosp->c_cflag & CSTOPB) != 0;
+        priv->baud  = cfgetispeed(termiosp);
+        ab32vg1_uart0_configure(priv->baud, priv->stop2);
+        ab32vg1_modifycon(priv, 0, UART_CON_RXIE);
+        break;
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+#else
+  return -ENOTTY;
+#endif
+}
+
+/****************************************************************************
+ * Name: ab32vg1_receive
+ *
+ * Description:
+ *   Called (usually) from the interrupt level to receive one character from
+ *   the UART.  Error bits associated with the receipt are provided in the
+ *   return 'status'.
+ *
+ ****************************************************************************/
+
+static int ab32vg1_receive(struct uart_dev_s *dev, unsigned int *status)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+  uint8_t ch;
+
+  ch = ab32vg1_serialin(priv, AB32VG1_UART_DATA_OFFSET) & 0xff;
+  ab32vg1_serialout(priv, AB32VG1_UART_CPND_OFFSET, UART_CPND_CRXPND);
+  *status = 0;
+
+#ifdef CONFIG_AB32VG1_UART0_ROMKEY
+  if (ch == g_romkey[priv->keymatch])
+    {
+      if (++priv->keymatch == ROMKEY_LEN)
+        {
+          up_systemreset();
+        }
+    }
+  else
+    {
+      priv->keymatch = (ch == g_romkey[0]) ? 1 : 0;
+    }
+#endif
+
+  return ch;
+}
+
+/****************************************************************************
+ * Name: ab32vg1_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+static void ab32vg1_rxint(struct uart_dev_s *dev, bool enable)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+  if (enable)
+    {
+      ab32vg1_modifycon(priv, 0, UART_CON_RXIE);
+    }
+  else
+#endif
+    {
+      ab32vg1_modifycon(priv, UART_CON_RXIE, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: ab32vg1_rxavailable
+ *
+ * Description:
+ *   Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+static bool ab32vg1_rxavailable(struct uart_dev_s *dev)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+
+  return (ab32vg1_serialin(priv, AB32VG1_UART_CON_OFFSET) &
+          UART_CON_RXPND) != 0;
+}
+
+/****************************************************************************
+ * Name: ab32vg1_send
+ *
+ * Description:
+ *   This method will send one byte on the UART.  Writing the data register
+ *   clears TXPND.
+ *
+ ****************************************************************************/
+
+static void ab32vg1_send(struct uart_dev_s *dev, int ch)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+
+  priv->txbusy = true;
+  ab32vg1_serialout(priv, AB32VG1_UART_DATA_OFFSET, (uint8_t)ch);
+}
+
+/****************************************************************************
+ * Name: ab32vg1_txint
+ *
+ * Description:
+ *   Call to enable or disable TX interrupts
+ *
+ ****************************************************************************/
+
+static void ab32vg1_txint(struct uart_dev_s *dev, bool enable)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+  if (enable)
+    {
+#ifndef CONFIG_SUPPRESS_SERIAL_INTS
+      ab32vg1_modifycon(priv, 0, UART_CON_TXIE);
+
+      /* If the transmitter is idle no TX interrupt will come: fake one by
+       * calling uart_xmitchars() with interrupts disabled.
+       */
+
+      if (!priv->txbusy)
+        {
+          uart_xmitchars(dev);
+        }
+#endif
+    }
+  else
+    {
+      ab32vg1_modifycon(priv, UART_CON_TXIE, 0);
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: ab32vg1_txready
+ *
+ * Description:
+ *   Return true if a new byte can be written: no byte in flight, or the
+ *   last one has been shifted out (TXPND).  The UART has no FIFO.
+ *
+ ****************************************************************************/
+
+static bool ab32vg1_txready(struct uart_dev_s *dev)
+{
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)dev->priv;
+
+  return !priv->txbusy ||
+         (ab32vg1_serialin(priv, AB32VG1_UART_CON_OFFSET) &
+          UART_CON_TXPND) != 0;
+}
+
+/****************************************************************************
+ * Name: ab32vg1_txempty
+ *
+ * Description:
+ *   Return true if all bytes have been sent
+ *
+ ****************************************************************************/
+
+static bool ab32vg1_txempty(struct uart_dev_s *dev)
+{
+  return ab32vg1_txready(dev);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+#ifdef USE_EARLYSERIALINIT
+
+/****************************************************************************
+ * Name: riscv_earlyserialinit
+ *
+ * Description:
+ *   Performs the low level UART initialization early in debug so that the
+ *   serial console will be available during boot up.  This must be called
+ *   before riscv_serialinit.
+ *
+ ****************************************************************************/
+
+void riscv_earlyserialinit(void)
+{
+#ifdef HAVE_CONSOLE
+  ab32vg1_setup(&CONSOLE_DEV);
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: riscv_serialinit
+ *
+ * Description:
+ *   Register serial console and serial ports.  This assumes
+ *   that riscv_earlyserialinit was called previously.
+ *
+ ****************************************************************************/
+
+void riscv_serialinit(void)
+{
+#ifdef HAVE_CONSOLE
+  uart_register("/dev/console", &CONSOLE_DEV);
+#endif
+
+  uart_register("/dev/ttyS0", &TTYS0_DEV);
+}
+
+/****************************************************************************
+ * Name: up_putc
+ *
+ * Description:
+ *   Provide priority, low-level access to support OS debug writes
+ *
+ ****************************************************************************/
+
+void up_putc(int ch)
+{
+#ifdef HAVE_CONSOLE
+  struct ab32vg1_uart_s *priv = (struct ab32vg1_uart_s *)CONSOLE_DEV.priv;
+  irqstate_t flags;
+
+  /* Let a byte sent by the interrupt driven path finish first.  If TX
+   * interrupts are enabled, the TXPND left by our byte resumes them.
+   */
+
+  flags = up_irq_save();
+  if (priv->txbusy)
+    {
+      while ((ab32vg1_serialin(priv, AB32VG1_UART_CON_OFFSET) &
+              UART_CON_TXPND) == 0)
+        {
+        }
+    }
+
+  riscv_lowputc(ch);
+  priv->txbusy = true;
+  up_irq_restore(flags);
+#endif
+}
+
+#else /* USE_SERIALDRIVER */
+
+/****************************************************************************
+ * Name: up_putc
+ *
+ * Description:
+ *   Provide priority, low-level access to support OS debug writes
+ *
+ ****************************************************************************/
+
+void up_putc(int ch)
+{
+  irqstate_t flags = up_irq_save();
+
+  riscv_lowputc(ch);
+  up_irq_restore(flags);
+}
+
+#endif /* USE_SERIALDRIVER */
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_start.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_start.c
new file mode 100644
index 00000000000..e0cc25e8ba6
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_start.c
@@ -0,0 +1,108 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_start.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include <nuttx/init.h>
+#include <arch/board/board.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "ab32vg1.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_FEATURES
+#  define showprogress(c) riscv_lowputc(c)
+#else
+#  define showprogress(c)
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: __ab32vg1_start
+ *
+ * Description:
+ *   C entry point, called from __start with the idle stack in place.  The
+ *   whole image (code and initialized data) has already been placed in RAM
+ *   by the loader, so only .bss needs clearing.
+ *
+ ****************************************************************************/
+
+void __ab32vg1_start(void)
+{
+  uint32_t *dest;
+
+  /* The watchdog may have been left running by the boot code */
+
+  putreg32(WDTCON_DISABLE, AB32VG1_WDTCON);
+
+  /* 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.
+   */
+
+  for (dest = (uint32_t *)_sbss; dest < (uint32_t *)_ebss; )
+    {
+      *dest++ = 0;
+    }
+
+  /* Setup the system clock */
+
+  ab32vg1_clockconfig();
+
+  /* Configure the UART so we can get debug output */
+
+  ab32vg1_lowsetup();
+
+  showprogress('A');
+
+#ifdef USE_EARLYSERIALINIT
+  riscv_earlyserialinit();
+#endif
+
+  showprogress('B');
+
+  /* Do board initialization */
+
+  ab32vg1_boardinitialize();
+
+  showprogress('C');
+
+  /* Call nx_start() */
+
+  nx_start();
+
+  /* Shouldn't get here */
+
+  for (; ; );
+}
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_systemreset.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_systemreset.c
new file mode 100644
index 00000000000..df4f1ea679f
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_systemreset.c
@@ -0,0 +1,54 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_systemreset.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/arch.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_systemreset
+ *
+ * Description:
+ *   Reset the chip with a 1 ms watchdog timeout.  After the reset the mask
+ *   ROM listens for its UART loader for a short time before booting from
+ *   flash, so this is also how the host gets back to the ROM loader.
+ *
+ ****************************************************************************/
+
+void up_systemreset(void)
+{
+  up_irq_save();
+  putreg32(WDTCON_RESET_1MS, AB32VG1_WDTCON);
+
+  for (; ; );
+}
diff --git a/arch/risc-v/src/ab32vg1/ab32vg1_timerisr.c 
b/arch/risc-v/src/ab32vg1/ab32vg1_timerisr.c
new file mode 100644
index 00000000000..232071cc629
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/ab32vg1_timerisr.c
@@ -0,0 +1,95 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/ab32vg1_timerisr.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <time.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <arch/board/board.h>
+
+#include "riscv_internal.h"
+#include "chip.h"
+#include "ab32vg1.h"
+#include "hardware/ab32vg1_timer.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* TMR0 counts system clock cycles and wraps at TMR0PR */
+
+#define TICK_RELOAD  (AB32VG1_SYSCLK_FREQUENCY / CLK_TCK)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ab32vg1_timerisr
+ ****************************************************************************/
+
+static int ab32vg1_timerisr(int irq, void *context, void *arg)
+{
+  putreg32(TMR_CPND_TPCLR, AB32VG1_TMR0CPND);
+
+  /* Process timer interrupt */
+
+  nxsched_process_timer();
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: up_timer_initialize
+ *
+ * Description:
+ *   This function is called during start-up to initialize the timer
+ *   interrupt.
+ *
+ ****************************************************************************/
+
+void up_timer_initialize(void)
+{
+  modifyreg32(AB32VG1_CLKGAT0, 0, CLKGAT0_TMR0);
+
+  putreg32(0, AB32VG1_TMR0CON);
+  putreg32(0, AB32VG1_TMR0CNT);
+  putreg32(TICK_RELOAD - 1, AB32VG1_TMR0PR);
+  putreg32(TMR_CPND_TPCLR, AB32VG1_TMR0CPND);
+
+  irq_attach(AB32VG1_IRQ_TMR0, ab32vg1_timerisr, NULL);
+
+  putreg32(TMR_CON_INCSEL_SYSCLK | TMR_CON_TIE | TMR_CON_TMREN,
+           AB32VG1_TMR0CON);
+
+  up_enable_irq(AB32VG1_IRQ_TMR0);
+}
diff --git a/arch/risc-v/src/ab32vg1/chip.h b/arch/risc-v/src/ab32vg1/chip.h
new file mode 100644
index 00000000000..05fb3cc7653
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/chip.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/chip.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_CHIP_H
+#define __ARCH_RISCV_SRC_AB32VG1_CHIP_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <arch/chip/chip.h>
+
+#include "hardware/ab32vg1_memorymap.h"
+#include "hardware/ab32vg1_pic.h"
+#include "hardware/ab32vg1_clk.h"
+
+#endif /* __ARCH_RISCV_SRC_AB32VG1_CHIP_H */
diff --git a/arch/risc-v/src/ab32vg1/hardware/ab32vg1_clk.h 
b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_clk.h
new file mode 100644
index 00000000000..5fb3c85b097
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_clk.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/hardware/ab32vg1_clk.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_CLK_H
+#define __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_CLK_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/ab32vg1_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Clock, PLL and watchdog registers.  The clock tree is not covered by the
+ * register manual; values follow the RT-Thread BSP (system_ab32vgx.c) and
+ * were checked on hardware by measuring TMR0 against the UART bit clock.
+ */
+
+#define AB32VG1_CLKCON0         AB32VG1_SFR(0, 0x19)
+#define AB32VG1_WDTCON          AB32VG1_SFR(0, 0x1a)
+#define AB32VG1_CLKCON1         AB32VG1_SFR(0, 0x1d)
+#define AB32VG1_SPI0BAUD        AB32VG1_SFR(0, 0x2c)
+#define AB32VG1_PLL0DIV         AB32VG1_SFR(3, 0x23)
+#define AB32VG1_PLL0CON         AB32VG1_SFR(3, 0x26)
+#define AB32VG1_CLKCON2         AB32VG1_SFR(3, 0x2a)
+#define AB32VG1_CLKGAT0         AB32VG1_SFR(3, 0x2c)
+#define AB32VG1_CLKCON3         AB32VG1_SFR(3, 0x2f)
+#define AB32VG1_CLKGAT1         AB32VG1_SFR(3, 0x3f)
+
+/* CLKCON0 */
+
+#define CLKCON0_SYSCLK_RC2M     (0 << 2)          /* [3:2] system clock */
+#define CLKCON0_SYSCLK_SPLL     (2 << 2)
+#define CLKCON0_SYSCLK_MASK     (3 << 2)
+#define CLKCON0_SPLL_PLL0       (0 << 4)          /* [6:4] SPLL source */
+#define CLKCON0_SPLL_XOSC26M    (4 << 4)
+#define CLKCON0_SPLL_MASK       (7 << 4)
+
+/* CLKCON1 */
+
+#define CLKCON1_UART_INC        (1 << 14)         /* uart_inc clock on */
+
+/* CLKCON2 */
+
+#define CLKCON2_SPLLDIV_SHIFT   8                 /* [12:8] SPLL divider */
+#define CLKCON2_SPLLDIV_MASK    (0x1f << CLKCON2_SPLLDIV_SHIFT)
+
+/* PLL0CON */
+
+#define PLL0CON_VCO_SHIFT       3                 /* [5:3] VCO band */
+#define PLL0CON_VCO_MASK        (7 << PLL0CON_VCO_SHIFT)
+#define PLL0CON_VCO_HIGH        (1 << PLL0CON_VCO_SHIFT) /* > 206 MHz */
+#define PLL0CON_ANALOG_EN       (1 << 6)
+#define PLL0CON_LDO_EN          (1 << 12)
+#define PLL0CON_SDM_EN          (1 << 18)
+#define PLL0CON_DIV_UPDATE      (1 << 20)
+
+/* PLL0 runs at 240 MHz from the 26 MHz crystal */
+
+#define AB32VG1_XOSC_FREQUENCY  26000000
+#define AB32VG1_PLL0_FREQUENCY  240000000
+#define AB32VG1_PLL0DIV_240M    (240 * 65536 / 26)
+
+/* CLKGAT0 / CLKGAT1 peripheral clock gates */
+
+#define CLKGAT0_TMR0            (1 << 8)
+#define CLKGAT0_SD0             (1 << 9)
+#define CLKGAT0_UART0           (1 << 10)
+#define CLKGAT0_HSUART          (1 << 11)
+#define CLKGAT0_SARADC          (1 << 13)
+#define CLKGAT0_USB             (1 << 14)
+#define CLKGAT0_PORT            (1 << 17)
+#define CLKGAT0_SPI0            (1 << 19)
+#define CLKGAT0_UART1           (1 << 21)
+#define CLKGAT0_TMR1            (1 << 24)
+#define CLKGAT0_TMR2            (1 << 25)
+#define CLKGAT1_TMR3            (1 << 8)
+#define CLKGAT1_TMR4            (1 << 9)
+#define CLKGAT1_TMR5            (1 << 10)
+#define CLKGAT1_UART2           (1 << 11)
+#define CLKGAT1_SPI1            (1 << 12)
+
+/* WDTCON (register manual, chapter 6).  The *_WR fields are write keys. */
+
+#define WDTCON_CLR              0x0000000a        /* Kick the watchdog */
+#define WDTCON_DISABLE          0x00000aa0        /* WDTEN=0, RSTEN=0 */
+#define WDTCON_RESET_1MS        0x0a000110        /* 1 ms timeout, reset */
+#define WDTCON_WDTPND           (1u << 31)
+
+#endif /* __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_CLK_H */
diff --git a/arch/risc-v/src/ab32vg1/hardware/ab32vg1_gpio.h 
b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_gpio.h
new file mode 100644
index 00000000000..3119de9e0e8
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_gpio.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/hardware/ab32vg1_gpio.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_GPIO_H
+#define __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_GPIO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/ab32vg1_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* GPIO ports A, B, E, F (register manual, chapter 1).  Each port has the
+ * same register layout; ports are 16 registers apart.
+ */
+
+#define AB32VG1_GPIOA_BASE      AB32VG1_SFR(6, 0x00)
+#define AB32VG1_GPIOB_BASE      AB32VG1_SFR(6, 0x10)
+#define AB32VG1_GPIOE_BASE      AB32VG1_SFR(6, 0x20)
+#define AB32VG1_GPIOF_BASE      AB32VG1_SFR(6, 0x30)
+
+#define AB32VG1_GPIO_SET_OFFSET     0x00   /* Write 1: output high */
+#define AB32VG1_GPIO_CLR_OFFSET     0x04   /* Write 1: output low */
+#define AB32VG1_GPIO_DATA_OFFSET    0x08   /* Pin level / output latch */
+#define AB32VG1_GPIO_DIR_OFFSET     0x0c   /* 1: input, 0: output */
+#define AB32VG1_GPIO_DE_OFFSET      0x10   /* 1: digital, 0: analog */
+#define AB32VG1_GPIO_FEN_OFFSET     0x14   /* 1: peripheral function */
+#define AB32VG1_GPIO_DRV_OFFSET     0x18   /* Drive strength */
+#define AB32VG1_GPIO_PU_OFFSET      0x1c   /* 10K pull-up */
+#define AB32VG1_GPIO_PD_OFFSET      0x20   /* 10K pull-down */
+#define AB32VG1_GPIO_PU200K_OFFSET  0x24
+#define AB32VG1_GPIO_PD200K_OFFSET  0x28
+#define AB32VG1_GPIO_PU300_OFFSET   0x2c
+#define AB32VG1_GPIO_PD300_OFFSET   0x30
+
+/* Function mapping (register manual, FUNCMCON0..2).  Writing 0xf to a
+ * 4-bit field clears it, writing another value selects a pin group; the
+ * other fields are not affected by writing 0.
+ */
+
+#define AB32VG1_FUNCMCON0       AB32VG1_SFR(0, 0x07)
+#define AB32VG1_FUNCMCON1       AB32VG1_SFR(0, 0x08)
+#define AB32VG1_FUNCMCON2       AB32VG1_SFR(0, 0x09)
+
+#define FUNCMCON0_SD0MAP_SHIFT    0
+#define FUNCMCON0_SPI0MAP_SHIFT   4
+#define FUNCMCON0_UT0TXMAP_SHIFT  8
+#define FUNCMCON0_UT0RXMAP_SHIFT  12
+#define FUNCMCON0_HSUTTXMAP_SHIFT 16
+#define FUNCMCON0_HSUTRXMAP_SHIFT 20
+#define FUNCMCON0_UT1TXMAP_SHIFT  24
+#define FUNCMCON0_UT1RXMAP_SHIFT  28
+
+#define FUNCMCON_CLEAR          0xf
+#define FUNCMCON_G(n)           (n)        /* Map to pin group n */
+#define FUNCMCON_RX_ON_TX       0x7        /* UARTx RX on its TX pin */
+
+#endif /* __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_GPIO_H */
diff --git a/arch/risc-v/src/ab32vg1/hardware/ab32vg1_memorymap.h 
b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_memorymap.h
new file mode 100644
index 00000000000..cce8e2ab613
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_memorymap.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/hardware/ab32vg1_memorymap.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_MEMORYMAP_H
+#define __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_MEMORYMAP_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* All peripheral registers live in the first 4 KB of the address space and
+ * are 32 bits wide.  They are grouped in 16 "SFR banks" of 64 registers.
+ */
+
+#define AB32VG1_SFR0_BASE   0x00000000
+#define AB32VG1_SFR1_BASE   0x00000100
+#define AB32VG1_SFR2_BASE   0x00000200
+#define AB32VG1_SFR3_BASE   0x00000300
+#define AB32VG1_SFR4_BASE   0x00000400
+#define AB32VG1_SFR5_BASE   0x00000500
+#define AB32VG1_SFR6_BASE   0x00000600
+#define AB32VG1_SFR7_BASE   0x00000700
+#define AB32VG1_SFR8_BASE   0x00000800
+#define AB32VG1_SFR9_BASE   0x00000900
+
+#define AB32VG1_SFR(b, n)   (AB32VG1_SFR##b##_BASE + ((n) << 2))
+
+#endif /* __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_MEMORYMAP_H */
diff --git a/arch/risc-v/src/ab32vg1/hardware/ab32vg1_pic.h 
b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_pic.h
new file mode 100644
index 00000000000..0c302437e88
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_pic.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/hardware/ab32vg1_pic.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_PIC_H
+#define __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_PIC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/ab32vg1_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Interrupt controller and trap registers.  These are not described in the
+ * register manual; the behaviour below was established by the probes in
+ * the port's bring-up journal and matches the RT-Thread BSP (ab32vg1.h).
+ */
+
+#define AB32VG1_CPU_STATUS      AB32VG1_SFR(5, 0x00) /* 0xc0000002 after an 
exception */
+#define AB32VG1_PICCONCLR       AB32VG1_SFR(5, 0x0c) /* Write 1 to clear 
PICCON bits */
+#define AB32VG1_PICCONSET       AB32VG1_SFR(5, 0x0d) /* Write 1 to set PICCON 
bits */
+#define AB32VG1_PICENCLR        AB32VG1_SFR(5, 0x0e)
+#define AB32VG1_PICENSET        AB32VG1_SFR(5, 0x0f)
+#define AB32VG1_PICCON          AB32VG1_SFR(5, 0x10) /* Control */
+#define AB32VG1_PICEN           AB32VG1_SFR(5, 0x11) /* Enable, 1 bit/source */
+#define AB32VG1_PICPR           AB32VG1_SFR(5, 0x12) /* Priority: 1 = high */
+#define AB32VG1_PICADR          AB32VG1_SFR(5, 0x13) /* Vector table base */
+#define AB32VG1_PICPND          AB32VG1_SFR(5, 0x14) /* Pending */
+#define AB32VG1_EPICCON         AB32VG1_SFR(5, 0x1e) /* PICCON.IE saved on 
trap */
+#define AB32VG1_EPC             AB32VG1_SFR(5, 0x1f) /* Trap return address */
+
+/* PICCON */
+
+#define PICCON_IE               (1 << 0)   /* Low priority interrupt enable */
+#define PICCON_EN               (1 << 1)   /* Interrupt unit enable */
+#define PICCON_INIT             0x00010002 /* As set by the RT-Thread BSP */
+
+/* Vector offsets from PICADR */
+
+#define AB32VG1_VEC_EXCEPTION   0x10       /* Exceptions, high prio IRQs */
+#define AB32VG1_VEC_IRQ         0x40       /* Low priority IRQs */
+#define AB32VG1_VEC_CACHE       0x80       /* XIP cache miss */
+
+/* Software interrupt: sets PICPND bit 2 */
+
+#define AB32VG1_SWINT_INSN      0xb0030057
+
+#endif /* __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_PIC_H */
diff --git a/arch/risc-v/src/ab32vg1/hardware/ab32vg1_timer.h 
b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_timer.h
new file mode 100644
index 00000000000..bc442a90b76
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_timer.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/hardware/ab32vg1_timer.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_TIMER_H
+#define __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_TIMER_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/ab32vg1_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Timer 0/1/2 (register manual, chapter 2) */
+
+#define AB32VG1_TMR0CON         AB32VG1_SFR(0, 0x14)
+#define AB32VG1_TMR0CPND        AB32VG1_SFR(0, 0x15)
+#define AB32VG1_TMR0CNT         AB32VG1_SFR(0, 0x16)
+#define AB32VG1_TMR0PR          AB32VG1_SFR(0, 0x17)
+
+#define AB32VG1_TMR1CON         AB32VG1_SFR(0, 0x35)
+#define AB32VG1_TMR1CPND        AB32VG1_SFR(0, 0x36)
+#define AB32VG1_TMR1CNT         AB32VG1_SFR(0, 0x37)
+#define AB32VG1_TMR1PR          AB32VG1_SFR(0, 0x38)
+
+#define AB32VG1_TMR2CON         AB32VG1_SFR(0, 0x3a)
+#define AB32VG1_TMR2CPND        AB32VG1_SFR(0, 0x3b)
+#define AB32VG1_TMR2CNT         AB32VG1_SFR(0, 0x3c)
+#define AB32VG1_TMR2PR          AB32VG1_SFR(0, 0x3d)
+
+/* TMRxCON */
+
+#define TMR_CON_TMREN           (1 << 0)          /* Timer enable */
+#define TMR_CON_INCSEL_SHIFT    2                 /* Count source */
+#define TMR_CON_INCSEL_SYSCLK   (0 << TMR_CON_INCSEL_SHIFT)
+#define TMR_CON_INCSRC          (1 << 6)          /* 1: external pin */
+#define TMR_CON_TIE             (1 << 7)          /* Overflow int enable */
+#define TMR_CON_TPND            (1 << 9)          /* Overflow pending */
+
+/* TMRxCPND */
+
+#define TMR_CPND_TPCLR          (1 << 9)
+
+#endif /* __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_TIMER_H */
diff --git a/arch/risc-v/src/ab32vg1/hardware/ab32vg1_uart.h 
b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_uart.h
new file mode 100644
index 00000000000..e9f334b3470
--- /dev/null
+++ b/arch/risc-v/src/ab32vg1/hardware/ab32vg1_uart.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+ * arch/risc-v/src/ab32vg1/hardware/ab32vg1_uart.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_UART_H
+#define __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_UART_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/ab32vg1_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* UART0/1/2 (register manual, chapter 8).  Each UART has the same four
+ * consecutive registers.
+ */
+
+#define AB32VG1_UART0_BASE      AB32VG1_SFR(0, 0x10)
+#define AB32VG1_UART1_BASE      AB32VG1_SFR(0, 0x30)
+#define AB32VG1_UART2_BASE      AB32VG1_SFR(9, 0x18)
+
+#define AB32VG1_UART_CON_OFFSET   0x00
+#define AB32VG1_UART_CPND_OFFSET  0x04
+#define AB32VG1_UART_BAUD_OFFSET  0x08
+#define AB32VG1_UART_DATA_OFFSET  0x0c
+
+/* UARTxCON */
+
+#define UART_CON_UTEN           (1 << 0)   /* UART enable */
+#define UART_CON_BIT9EN         (1 << 1)   /* 9-bit mode */
+#define UART_CON_RXIE           (1 << 2)   /* RX interrupt enable */
+#define UART_CON_TXIE           (1 << 3)   /* TX interrupt enable */
+#define UART_CON_SB2EN          (1 << 4)   /* 2 stop bits */
+#define UART_CON_CLKSRC         (1 << 5)   /* 0: system clock, 1: uart_inc */
+#define UART_CON_ONELINE        (1 << 6)   /* TX/RX on one pin */
+#define UART_CON_RXEN           (1 << 7)   /* RX enable */
+#define UART_CON_TXPND          (1 << 8)   /* TX of one byte finished */
+#define UART_CON_RXPND          (1 << 9)   /* RX of one byte finished */
+
+/* UARTxCPND */
+
+#define UART_CPND_CTXPND        (1 << 8)
+#define UART_CPND_CRXPND        (1 << 9)
+#define UART_CPND_CKEYPND       (1 << 16)
+#define UART_CPND_CRSTKEYPND    (1 << 17)
+
+/* UARTxBAUD: baud = Fclk / (n + 1); RX in [31:16], TX in [15:0].  With
+ * CLKSRC = 1 the clock is uart_inc = XOSC 26 MHz / 2.
+ */
+
+#define UART_INC_FREQUENCY      13000000
+#define UART_BAUD(n)            (((uint32_t)(n) << 16) | (uint32_t)(n))
+
+#endif /* __ARCH_RISCV_SRC_AB32VG1_HARDWARE_AB32VG1_UART_H */
diff --git a/arch/risc-v/src/common/CMakeLists.txt 
b/arch/risc-v/src/common/CMakeLists.txt
index e2b8f8bb902..7b09b378c51 100644
--- a/arch/risc-v/src/common/CMakeLists.txt
+++ b/arch/risc-v/src/common/CMakeLists.txt
@@ -22,12 +22,14 @@
 
 set(SRCS)
 
-list(APPEND SRCS riscv_exception_common.S riscv_vectors.S)
-list(APPEND SRCS riscv_saveusercontext.S)
+if(NOT CONFIG_ARCH_RV_CUSTOM_TRAP)
+  list(APPEND SRCS riscv_exception_common.S riscv_vectors.S)
+  list(APPEND SRCS riscv_saveusercontext.S riscv_getnewintctx.c)
+endif()
 list(APPEND SRCS riscv_allocateheap.c riscv_cpuidlestack.c)
 list(APPEND SRCS riscv_cpuinfo.c riscv_createstack.c riscv_doirq.c
      riscv_exception.c)
-list(APPEND SRCS riscv_exit.c riscv_getintstack.c riscv_getnewintctx.c)
+list(APPEND SRCS riscv_exit.c riscv_getintstack.c)
 list(APPEND SRCS riscv_initialize.c riscv_initialstate.c riscv_modifyreg32.c)
 list(APPEND SRCS riscv_nputs.c riscv_registerdump.c)
 list(APPEND SRCS riscv_releasestack.c)
diff --git a/arch/risc-v/src/common/Make.defs b/arch/risc-v/src/common/Make.defs
index 4b46fc9ccad..1899442bfa3 100644
--- a/arch/risc-v/src/common/Make.defs
+++ b/arch/risc-v/src/common/Make.defs
@@ -22,19 +22,27 @@
 
 STARTUP_OBJS = crt0$(OBJEXT)
 
-# Specify our general Assembly files
+# Specify our general Assembly files.  Chips without the standard trap
+# architecture provide their own trap entry and context helpers.
+
+ifneq ($(CONFIG_ARCH_RV_CUSTOM_TRAP),y)
 CMN_ASRCS += riscv_vectors.S riscv_exception_common.S
 CMN_ASRCS += riscv_saveusercontext.S
+endif
 
 # Specify C code within the common directory to be included
 CMN_CSRCS += riscv_initialize.c riscv_swint.c
 CMN_CSRCS += riscv_allocateheap.c riscv_createstack.c riscv_cpuinfo.c
 CMN_CSRCS += riscv_cpuidlestack.c riscv_doirq.c riscv_exit.c riscv_exception.c
-CMN_CSRCS += riscv_getnewintctx.c riscv_getintstack.c riscv_initialstate.c
+CMN_CSRCS += riscv_getintstack.c riscv_initialstate.c
 CMN_CSRCS += riscv_modifyreg32.c riscv_nputs.c riscv_releasestack.c
 CMN_CSRCS += riscv_registerdump.c riscv_stackframe.c riscv_switchcontext.c
 CMN_CSRCS += riscv_usestack.c riscv_tcbinfo.c
 
+ifneq ($(CONFIG_ARCH_RV_CUSTOM_TRAP),y)
+CMN_CSRCS += riscv_getnewintctx.c
+endif
+
 ifeq ($(CONFIG_ENABLE_ALL_SIGNALS),y)
 CMN_CSRCS += riscv_schedulesigaction.c riscv_sigdeliver.c
 endif

Reply via email to