Apache NuttX RTOS on RISC-V: Star64 JH7110 SBC

📝 9 Jul 2023

Pine64 Star64 64-bit RISC-V SBC

In this article we’ll boot a tiny bit of Apache NuttX RTOS on the Pine64 Star64 64-bit RISC-V Single-Board Computer.

(Based on StarFive JH7110, the same SoC in VisionFive2)

What’s NuttX?

Apache NuttX is a Real-Time Operating System (RTOS) that runs on many kinds of devices, from 8-bit to 64-bit.

NuttX supports Star64?

Nope NuttX won’t run on Star64 yet, we’ll hit some interesting (and highly educational) RISC-V challenges.

But the things that we learn today will be super helpful for porting NuttX to Star64.

Please join me as we whip up a tiny tasty treat of NuttX on Star64…

Apache NuttX RTOS on 64-bit QEMU RISC-V Emulator

§1 Start with QEMU Emulator

Earlier we successfully tested NuttX RTOS on QEMU Emulator for 64-bit RISC-V (pic above)…

Let’s run this on Star64! Starting with the NuttX Boot Code (in RISC-V Assembly)…

Surely we’ll run into problems?

Fortunately we have a Serial Debug Console connected to Star64. (Pic below)

We’ll print some Debug Logs as NuttX boots on Star64. Here’s our plan…

Our cooking begins…

Star64 SBC with Woodpecker USB Serial Adapter

Star64 with Woodpecker USB Serial Adapter

§2 Print to QEMU Console

We’re printing to the Serial Console on QEMU Emulator…

What’s the UART Controller in QEMU?

We check the NuttX Build Configuration for QEMU: nsh64/defconfig

CONFIG_16550_ADDRWIDTH=0
CONFIG_16550_UART0=y
CONFIG_16550_UART0_BASE=0x10000000
CONFIG_16550_UART0_CLOCK=3686400
CONFIG_16550_UART0_IRQ=37
CONFIG_16550_UART0_SERIAL_CONSOLE=y
CONFIG_16550_UART=y

This says that QEMU emulates a 16550 UART Controller.

And the Base Address of QEMU’s UART Controller is 0x1000 0000.

How to print to the 16550 UART Port?

We check the NuttX Driver for 16550 UART: uart_16550.c

// Send one byte to 16550 UART
static void u16550_send(struct uart_dev_s *dev, int ch) {

  // Fetch the 16550 Struct
  FAR struct u16550_s *priv = (FAR struct u16550_s *)dev->priv;

  // Print to 16550 UART...
  u16550_serialout(
    priv,                  // 16550 Struct
    UART_THR_OFFSET,       // Offset of Transmit Holding Register
    (uart_datawidth_t) ch  // Character to print
  );
}

(u16550_serialout is defined here)

To print a character, the driver writes to the UART Base Address 0x1000 0000 at Offset UART_THR_OFFSET.

And we discover that UART_THR_OFFSET is 0: uart_16550.h

#define UART_THR_INCR 0 /* Transmit Holding Register (when DLAB is 0) */
#define UART_THR_OFFSET (CONFIG_16550_REGINCR*UART_THR_INCR)

Which means that we can print to QEMU Console by writing to 0x1000 0000. How convenient!

// Print `1` to QEMU Console
*(volatile uint8_t *) 0x10000000 = '1';

What about RISC-V Assembly?

This is how we print to QEMU Console in RISC-V Assembly (to debug our NuttX Boot Code)…

/* Load UART Base Address to Register t0 */
li  t0, 0x10000000

/* Load `1` to Register t1 */
li  t1, 0x31
/* Store byte from Register t1 to UART Base Address, Offset 0 */
sb  t1, 0(t0)

/* Load `2` to Register t1 */
li  t1, 0x32
/* Store byte from Register t1 to UART Base Address, Offset 0 */
sb  t1, 0(t0)

/* Load `3` to Register t1 */
li  t1, 0x33
/* Store byte from Register t1 to UART Base Address, Offset 0 */
sb  t1, 0(t0)

(Previously here)

(li loads a Value into a Register)

(sb stores a byte from a Register into an Address)

When we start QEMU Emulator, our RISC-V Assembly prints “123” to the QEMU Console (pic below)…

## Remove `-bios none` for newer versions of NuttX
$ qemu-system-riscv64 \
  -semihosting \
  -M virt,aclint=on \
  -cpu rv64 \
  -bios none \
  -kernel nuttx \
  -nographic

123123123123123123112323
NuttShell (NSH) NuttX-12.0.3
nsh> 

123” is printed 8 times because QEMU is running with 8 CPUs.

Now we sprinkle some Debug Logs on Star64…

NuttX prints to QEMU Console

§3 UART Controller on Star64

What’s the UART Controller in Star64?

Star64 JH7110 uses the 8250 UART Controller, according to…

Which is compatible with the 16550 UART Controller used by QEMU.

But what’s the UART Base Address for Star64?

UART0 Base Address is at 0x1000 0000, according to…

Isn’t that the same UART Base Address as QEMU?

Yep! Earlier we saw the UART Base Address for NuttX QEMU: nsh64/defconfig

CONFIG_16550_UART0_BASE=0x10000000

0x1000 0000 is the exact same UART Base Address for QEMU AND Star64…

So no changes needed, our Debug Log will work on Star64!

(Star64 uses a different CONFIG_16550_REGINCR)

To boot NuttX on Star64, it needs a special ingredient…

Armbian Kernel Image

Kernel Header for RISC-V Armbian Linux

§4 RISC-V Linux Kernel Header

How will Star64 boot NuttX?

Star64’s U-Boot Bootloader will load NuttX Kernel into RAM and run it.

But we need to embed the RISC-V Linux Kernel Header (and pretend we’re Linux)…

Thus we cooked up this Assembly Code for our RISC-V Linux Header: jh7110_head.S

c.li    s4, -13              /* Magic Signature "MZ" (2 bytes) */
.long   0                    /* Executable Code padded to 8 bytes */
.quad   0x200000             /* Image load offset from start of RAM */
.quad   _ebss - __start      /* Effective size of kernel image */
.quad   0                    /* Kernel flags, little-endian */
.long   2                    /* Version of this header */
.long   0                    /* Reserved */
.quad   0                    /* Reserved */
.ascii  "RISCV\x00\x00\x00"  /* Magic number, "RISCV" (8 bytes) */
.ascii  "RSC\x05"            /* Magic number 2, "RSC\x05" (4 bytes) */
.long   0                    /* Reserved for PE COFF offset */

real_start:
  /* Actual Boot Code starts here... */

(c.li emits the Magic Signature “MZ”)

Note that Image Load Offset must be 0x20 0000

.quad  0x200000  /* Image load offset from start of RAM */

That’s because our NuttX Kernel will start at RAM Address 0x4020 0000. Chew on it for a bit…

§5 Start Address of NuttX Kernel

What’s this magical address 0x4020 0000?

From previous articles, we saw that Star64’s U-Boot Bootloader will load Linux Kernels into RAM at Address 0x4020 0000

Thus we do the same for NuttX on Star64.

This is how we set the Start Address to 0x4020 0000 in our NuttX Build Configuration: nsh/defconfig

// TODO: Fix CONFIG_RAM_SIZE
CONFIG_RAM_SIZE=33554432
CONFIG_RAM_START=0x40200000

And our NuttX Linker Script: ld.script

MEMORY
{
  /* Previously 0x80000000 */
  kflash (rx) : ORIGIN = 0x40200000, LENGTH = 2048K   /* w/ cache */
}
...
SECTIONS
{
  /* Previously 0x80000000 */
  . = 0x40200000;
  .text :

(Also knsh64/defconfig and ld-kernel64.script)

We’re sure this is correct?

We check the RISC-V Disassembly of our NuttX Kernel: nuttx.S

0000000040200000 <__start>:
  li  s4, -0xd    /* Magic Signature "MZ" (2 bytes) */
    40200000: 5a4d  li s4,-13
  j   real_start  /* Jump to Kernel Start (2 bytes) */
    40200002: a83d  j  40200040 <real_start>

The Start Address is indeed 0x4020 0000.

Yep Looks Good To Us (YLGTU), we’re ready to serve it on Star64!

Boot NuttX on Star64

§6 Boot NuttX on Star64

We’re finally ready to boot NuttX on Star64! We compile NuttX for RISC-V QEMU with these steps…

Then we tweak it to boot on Star64 (and rebuild)…

  1. Print the Debug Logs in RISC-V Assembly

  2. Check the UART Base Address

  3. Embed the RISC-V Kernel Header

  4. Set the Start Address of NuttX Kernel

This produces the NuttX ELF Image for Star64…

How to boot NuttX on microSD?

For the microSD Image, we start with this Armbian Image for Star64

Uncompress the .xz file. Write the .img file to a microSD Card with Balena Etcher or GNOME Disks.

The Device Tree is missing, so we fix it…

## Fix the Missing Device Tree
sudo chmod go+w /run/media/$USER/armbi_root/boot
sudo chmod go+w /run/media/$USER/armbi_root/boot/dtb/starfive
cp \
  /run/media/$USER/armbi_root/boot/dtb/starfive/jh7110-visionfive-v2.dtb \
  /run/media/$USER/armbi_root/boot/dtb/starfive/jh7110-star64-pine64.dtb

Then we overwrite the Linux Kernel Image by our NuttX Binary Image

## We assume that `nuttx` contains the NuttX ELF Image.
## Export the NuttX Binary Image to `nuttx.bin`
riscv64-unknown-elf-objcopy \
  -O binary \
  nuttx \
  nuttx.bin

## Delete Linux Kernel `/boot/Image`
rm /run/media/$USER/armbi_root/boot/Image

## Copy `nuttx.bin` to Linux Kernel `/boot/Image`
cp nuttx.bin /run/media/$USER/armbi_root/boot/Image

Insert the microSD Card into Star64 and power up.

(Or boot NuttX over the Network)

When it boots on Star64, NuttX prints “123” yay! (Pic above)

Starting kernel ...
clk u5_dw_i2c_clk_core already disabled
clk u5_dw_i2c_clk_apb already disabled
123

(Printed by our Boot Code)

But NuttX crashes with a RISC-V Illegal Instruction Exception

Unhandled exception: Illegal instruction
EPC: 000000004020005c RA: 00000000fff471c6 TVAL: 00000000f1402573
EPC: ffffffff804ba05c RA: 00000000402011c6 reloc adjusted

SP:  00000000ff733630 GP:  00000000ff735e00 TP:  0000000000000001
T0:  0000000010000000 T1:  0000000000000033 T2:  7869662e6b637366
S0:  0000000000000400 S1:  00000000ffff1428 A0:  0000000000000001
A1:  0000000046000000 A2:  0000000000000600 A3:  0000000000004000

(See the Complete Log)

(EPC is the Program Counter for the Exception: 0x4020 005C)

And Star64 (OpenSBI) shows the offending RISC-V Machine Code (in brackets)…

Code:
  0313 0320 8023 0062 0313 0330 8023 0062
  (2573 f140)
resetting ...
reset not supported yet
### ERROR ### Please RESET the board ###

Why did NuttX crash at 0x4020 005C? We percolate our code…

Cody AI Assistant tries to explain our RISC-V Exception

Cody AI Assistant tries to explain our RISC-V Exception

§7 NuttX Fails To Get Hart ID

What’s at 0x4020 005C?

Why did it crash NuttX?

We look up our NuttX RISC-V Disassembly nuttx.S and see this in our Boot Code: qemu_rv_head.S

nuttx/arch/risc-v/src/chip/qemu_rv_head.S:95
  /* Load the Hart ID (CPU ID) */
  csrr a0, mhartid
    4020005c: f1402573  csrr a0, mhartid

Breaking it down…

/* Load the Hart ID (CPU ID) */
csrr a0, mhartid

So the above code will load the Hart ID (or CPU ID) into Register A0.

(As explained here)

But it worked perfectly on QEMU! Why did it fail?

Ah that’s because something super spicy has changed on Star64: Our Privilege Level…

RISC-V Privilege Levels

§8 RISC-V Privilege Levels

What’s this Privilege Level?

RISC-V Machine Code runs at three Privilege Levels

NuttX on Star64 runs in Supervisor Mode. Which doesn’t allow access to Machine-Mode CSR Registers. (Pic above)

Remember this?

/* Load the Hart ID (CPU ID) */
csrr a0, mhartid

The m in mhartid signifies that it’s a Machine-Mode Register.

That’s why NuttX fails to read the Hart ID!

What runs in Machine Mode?

OpenSBI (Supervisor Binary Interface) is the first thing that boots on Star64.

It runs in Machine Mode and starts the U-Boot Bootloader.

(More about OpenSBI)

What about U-Boot Bootloader?

U-Boot Bootloader runs in Supervisor Mode. And starts NuttX, also in Supervisor Mode.

Thus OpenSBI is the only thing that runs in Machine Mode. And can access the Machine-Mode Registers. (Pic above)

(More about U-Boot)

QEMU doesn’t have this problem?

Because QEMU runs NuttX in (super-powerful) Machine Mode!

NuttX QEMU runs in Machine Mode

NuttX needs to fetch the Hart ID with a different recipe…

OpenSBI starts U-Boot Bootloader on Star64

OpenSBI starts U-Boot Bootloader on Star64

§9 Downgrade NuttX to Supervisor Mode

OpenSBI runs in Machine Mode and reads the Hart ID (CPU ID)…

How will NuttX get the Hart ID from OpenSBI?

Thankfully OpenSBI will pass the Hart ID to NuttX through Register A0.

So this (overly-powerful) line in our NuttX Boot Code

/* Load the Hart ID (CPU ID) */
csrr a0, mhartid

Gets demoted to this: jh7110_head.S

/* We assume that OpenSBI has passed Hart ID (value 1) in Register A0. */
/* But NuttX expects Hart ID to start at 0, so we subtract 1. */
addi a0, a0, -1

(OpenSBI passes Hart ID as 1, instead of 0)

(addi adds an Immediate Value to a Register)

What about other CSR Instructions in our NuttX Boot Code?

Easy! We change the Machine-Mode m Registers to Supervisor-Mode s Registers…

Time to taste this…

After fixing NuttX for Supervisor Mode

§10 Fix the NuttX Boot Code

From the previous section, we identified these fixes to run our NuttX Boot Code in Supervisor Mode

  1. Remove mhartid because OpenSBI will pass Hart ID in Register A0

  2. Subtract 1 from Register A0 because NuttX expects Hart ID to start with 0

  3. To Disable Interrupts: Change mie to sie

  4. To Load Trap Vector Table: Change mtvec to stvec

Here’s the updated NuttX Boot Code and our analysis…

What happens when we run this?

When we boot the modified NuttX on Star64, we see this (pic above)…

Starting kernel ...
clk u5_dw_i2c_clk_core already disabled
clk u5_dw_i2c_clk_apb already disabled
123067

(Source)

Now we’re smokin’ hot…

Why? Stay tuned for more tantalising treats in the next article!

Pine64 Star64 64-bit RISC-V SBC

§11 What’s Next

I hope this has been an Educational Experience on booting a fresh new OS for a 64-bit RISC-V SBC…

This is the first in a series of (yummy) articles on porting NuttX to Star64, please join me next time…

Many Thanks to my GitHub Sponsors for supporting my work! This article wouldn’t have been possible without your support.

Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…

lupyuen.github.io/src/nuttx2.md

RISC-V Privilege Levels

§12 Appendix: Hart ID from OpenSBI

NuttX can’t read the mhartid CSR Register in Supervisor Mode…

How to get the Hart ID from OpenSBI?

NuttX failed to read the mhartid CSR Register because it’s a Machine-Mode Register (pic above)…

Let’s figure out how Linux gets the Hart ID from OpenSBI. We refer to the Linux Boot Code: linux/arch/riscv/kernel/head.S

(Tip: CONFIG_RISCV_M_MODE is False and CONFIG_EFI is True)

/* Save hart ID and DTB physical address */
mv s0, a0
mv s1, a1

Here we see that U-Boot (or OpenSBI) will pass 2 arguments when it starts the kernel…

So we’ll simply read the Hart ID from Register A0. (And ignore A1)

What are the actual values of Registers A0 and A1?

Thanks to our Crash Dump, we know the actual values of A0 and A1!

SP:  00000000ff733630 GP:  00000000ff735e00 TP:  0000000000000001
T0:  0000000010000000 T1:  0000000000000033 T2:  7869662e6b637366
S0:  0000000000000400 S1:  00000000ffff1428 A0:  0000000000000001
A1:  0000000046000000 A2:  0000000000000600 A3:  0000000000004000

This says that…

Yep looks correct! But we’ll subtract 1 from Register A0 because NuttX expects Hart ID to start with 0.

We’ll see this implementation in our modified NuttX Boot Code. (Next section)

Platform-Level Interrupt Controller in JH7110 (U74) SoC

Why does OpenSBI return Hart ID 1? (Instead of 0)

According to the SiFive U74 Manual (Page 96), there are 5 RISC-V Cores in JH7110 (pic above)…

OpenSBI boots on the First Application Core

Which is why OpenSBI returns Hart ID 1. (Instead of 0)

(Though we pass the Hart ID to NuttX as Hart 0, since NuttX expects Hart ID to start at 0)

The Linux Boot Code looks confusing. What are CSR_IE and CSR_IP?

/* Mask all interrupts */
csrw CSR_IE, zero
csrw CSR_IP, zero

(Source)

That’s because the Linux Boot Code will work for Machine Mode AND Supervisor Mode! Here’s how CSR_IE and CSR_IP are mapped to the m and s CSR Registers…

(Remember: CONFIG_RISCV_M_MODE is false for NuttX)

#ifdef CONFIG_RISCV_M_MODE
  /* Use Machine-Mode CSR Registers */
  # define CSR_IE		CSR_MIE
  # define CSR_IP		CSR_MIP
  ...
#else
  /* Use Supervisor-Mode CSR Registers */
  # define CSR_IE		CSR_SIE
  # define CSR_IP		CSR_SIP
  ...
#endif /* !CONFIG_RISCV_M_MODE */

(Source)

After fixing NuttX for Supervisor Mode

§13 Appendix: NuttX in Supervisor Mode

Earlier we identified these fixes for the NuttX Boot Code to run in Supervisor Mode…

  1. Remove mhartid because OpenSBI will pass Hart ID in Register A0

  2. Subtract 1 from Register A0 because NuttX expects Hart ID to start with 0

  3. To Disable Interrupts: Change mie to sie

  4. To Load Trap Vector Table: Change mtvec to stvec

Below is the updated NuttX Boot Code for Supervisor Mode, and our analysis: jh7110_head.S

For All Hart IDs:

We receive the Hart ID from OpenSBI, subtract 1 and print it…

real_start:
  ...
  /* Load mhartid (cpuid) */
  /* Previously: csrr a0, mhartid */

  /* We assume that OpenSBI has passed Hart ID (value 1) in Register a0. */
  /* But NuttX expects Hart ID to start at 0, so we subtract 1. */
  addi a0, a0, -1

(Source)

(RISC-V Instructions explained)

If Hart ID is 0 (First CPU):

Set Stack Pointer to the Idle Thread Stack…

  /* Set stack pointer to the idle thread stack */
  bnez a0, 1f
  la   sp, QEMU_RV_IDLESTACK_TOP
  j    2f

(Source)

If Hart ID is 1, 2, 3, …

1:
  /* Load the number of CPUs that the kernel supports */
#ifdef CONFIG_SMP
  li   t1, CONFIG_SMP_NCPUS
#else
  li   t1, 1
#endif

  /* If a0 (mhartid) >= t1 (the number of CPUs), stop here */
  blt  a0, t1, 3f
  csrw sie, zero
  /* Previously: csrw mie, zero */
  wfi

3:
  /* To get g_cpu_basestack[mhartid], must get g_cpu_basestack first */
  la   t0, g_cpu_basestack

  /* Offset = pointer width * hart id */
#ifdef CONFIG_ARCH_RV32
  slli t1, a0, 2
#else
  slli t1, a0, 3
#endif
  add  t0, t0, t1

  /* Load idle stack base to sp */
  REGLOAD sp, 0(t0)

  /*
   * sp (stack top) = sp + idle stack size - XCPTCONTEXT_SIZE
   *
   * Note: Reserve some space used by up_initial_state since we are already
   * running and using the per CPU idle stack.
   */
  li   t0, STACK_ALIGN_UP(CONFIG_IDLETHREAD_STACKSIZE - XCPTCONTEXT_SIZE)
  add  sp, sp, t0

(Source)

For All Hart IDs:

2:
  /* Disable all interrupts (i.e. timer, external) in mie */
  csrw	sie, zero
  /* Previously: csrw	mie, zero */

  /* Load the Trap Vector Table */
  la   t0, __trap_vec
  csrw stvec, t0
  /* Previously: csrw mtvec, t0 */

  /* Jump to jh7110_start */
  jal  x1, jh7110_start

  /* We shouldn't return from _start */

(Source)

Note that we don’t load the Trap Vector Table, because we’ll use OpenSBI for Crash Logging.

(Like when we hit Exceptions with Machine-Mode Instructions)

Building Apache NuttX RTOS in 4 minutes

§14 Appendix: NuttX Start Address

Previously we changed the NuttX Start Address in nsh/defconfig and ld.script

Remember to change this Linker Script since we’re building for NuttX Kernel Mode: ld.script

MEMORY
{
  /* Previously 0x80000000 */
  kflash (rx) : ORIGIN = 0x40200000, LENGTH = 2048K   /* w/ cache */
  /* Previously 0x80200000 */
  ksram (rwx) : ORIGIN = 0x40400000, LENGTH = 2048K   /* w/ cache */
  /* Previously 0x80400000 */
  pgram (rwx) : ORIGIN = 0x40600000, LENGTH = 4096K   /* w/ cache */
}
...
SECTIONS
{
  /* Previously 0x80000000 */
  . = 0x40200000;
  .text :

Which should match the NuttX Build Configuration for Kernel Mode: nsh/defconfig

CONFIG_ARCH_PGPOOL_PBASE=0x40600000
CONFIG_ARCH_PGPOOL_VBASE=0x40600000
// TODO: Fix CONFIG_RAM_SIZE
CONFIG_RAM_SIZE=1048576
CONFIG_RAM_START=0x40200000

Why will we use NuttX Kernel Mode?

## Configure NuttX Build for Kernel Mode
tools/configure.sh rv-virt:knsh64

(Source)

We use rv-virt:knsh64 (NuttX Kernel Mode) instead of rv-virt:nsh64 (NuttX Flat Mode) so that NuttX will run in RISC-V Supervisor Mode. (Instead of RISC-V Machine Mode)

More about this in the next article.

Boot NuttX on Star64

§15 Appendix: NuttX Crash Log

Earlier we ran NuttX QEMU on Star64 (before fixing for Supervisor Mode) and it crashed…

Here’s the Crash Log dumped by OpenSBI…

Retrieving file: /boot/extlinux/extlinux.conf
383 bytes read in 7 ms (52.7 KiB/s)
1:Armbian
Retrieving file: /boot/uInitrd
10911538 bytes read in 466 ms (22.3 MiB/s)
Retrieving file: /boot/Image
163201 bytes read in 14 ms (11.1 MiB/s)
append: root=UUID=99f62df4-be35-475c-99ef-2ba3f74fe6b5 console=ttyS0,115200n8 console=tty0 earlycon=sbi rootflags=data=writeback stmmaceth=chain_mode:1 rw rw no_console_suspend consoleblank=0 fsck.fix=yes fsck.repair=yes net.ifnames=0 splash plymouth.ignore-serial-consoles
Retrieving file: /boot/dtb/starfive/jh7110-star64-pine64.dtb
50235 bytes read in 14 ms (3.4 MiB/s)
## Loading init Ramdisk from Legacy Image at 46100000 ...
   Image Name:   uInitrd
   Image Type:   RISC-V Linux RAMDisk Image (gzip compressed)
   Data Size:    10911474 Bytes = 10.4 MiB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 46000000
   Booting using the fdt blob at 0x46000000
   Using Device Tree in place at 0000000046000000, end 000000004600f43a

Starting kernel ...

clk u5_dw_i2c_clk_core already disabled
clk u5_dw_i2c_clk_apb already disabled
123Unhandled exception: Illegal instruction
EPC: 000000004020005c RA: 00000000fff471c6 TVAL: 00000000f1402573
EPC: ffffffff804ba05c RA: 00000000402011c6 reloc adjusted

SP:  00000000ff733630 GP:  00000000ff735e00 TP:  0000000000000001
T0:  0000000010000000 T1:  0000000000000033 T2:  7869662e6b637366
S0:  0000000000000400 S1:  00000000ffff1428 A0:  0000000000000001
A1:  0000000046000000 A2:  0000000000000600 A3:  0000000000004000
A4:  0000000000000000 A5:  0000000040200000 A6:  00000000fffd5708
A7:  0000000000000000 S2:  00000000fff47194 S3:  0000000000000003
S4:  fffffffffffffff3 S5:  00000000fffdbb50 S6:  0000000000000000
S7:  0000000000000000 S8:  00000000fff47194 S9:  0000000000000002
S10: 0000000000000000 S11: 0000000000000000 T3:  0000000000000023
T4:  000000004600b5cc T5:  000000000000ff00 T6:  000000004600b5cc

Code: 0313 0320 8023 0062 0313 0330 8023 0062 (2573 f140)


resetting ...
reset not supported yet
### ERROR ### Please RESET the board ###