📝 9 Jul 2023
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…
Migrate NuttX from QEMU Emulator to Real Hardware
Log to the Serial Console in RISC-V Assembly
Trick U-Boot Bootloader into thinking we’re Linux
Downgrade from Machine to Supervisor Privilege Level
With a little help from OpenSBI Supervisor Interface
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…
Check the Serial Console on QEMU Emulator, how it’s wired up
Test our Debug Log on QEMU Emulator
Port our Debug Log to Star64
Our cooking begins…
Star64 with Woodpecker USB Serial Adapter
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)
(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…
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…
Kernel Header for RISC-V Armbian Linux
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…
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!
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)…
Print the Debug Logs in RISC-V Assembly
Check the UART Base Address
Embed the RISC-V Kernel Header
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
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
(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
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
csrr
is the RISC-V Instruction that reads the Control and Status Register
(Which contains the CPU ID)
a0
is the RISC-V Register that will be loaded with the CPU ID
mhartid
says that we’ll read from the Hart ID Register, containing the ID of the Hardware Thread (“Hart”) that’s running our code.
(Equivalent to CPU ID)
So the above code will load the Hart ID (or CPU ID) into Register A0.
But it worked perfectly on QEMU! Why did it fail?
Ah that’s because something super spicy has changed on Star64: Our Privilege Level…
What’s this Privilege Level?
RISC-V Machine Code runs at three Privilege Levels…
M: Machine Mode (Most powerful)
S: Supervisor Mode (Less powerful)
U: User Mode (Least powerful)
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.
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)
QEMU doesn’t have this problem?
Because QEMU runs NuttX in (super-powerful) Machine Mode!
NuttX needs to fetch the Hart ID with a different recipe…
OpenSBI starts U-Boot Bootloader on Star64
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…
To Disable Interrupts: Change mie
to sie
/* Disable all interrupts (i.e. timer, external) */
csrw sie, zero
/* Previously `mie` */
To Load Trap Vector Table: Change mtvec
to stvec
/* Load address of Trap Vector Table */
csrw stvec, t0
/* Previously `mtvec` */
Time to taste this…
From the previous section, we identified these fixes to run our NuttX Boot Code in Supervisor Mode…
Remove mhartid
because OpenSBI will pass Hart ID in Register A0
Subtract 1 from Register A0 because NuttX expects Hart ID to start with 0
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
Now we’re smokin’ hot…
No more Crash Dump!
“0
” is the Adjusted Hart ID passed by OpenSBI to NuttX
“7
” is the last thing that’s printed by our NuttX Boot Code…
/* Print `7` */
li t0, 0x10000000
li t1, 0x37
sb t1, 0(t0)
Before jumping to the Start Code…
/* Jump to jh7110_start */
jal x1, jh7110_start
Which means that our NuttX Boot Code has completed execution yay!
But NuttX hangs in the C Function jh7110_start
Why? Stay tuned for more tantalising treats in the next article!
I hope this has been an Educational Experience on booting a fresh new OS for a 64-bit RISC-V SBC…
We migrated NuttX RTOS from QEMU Emulator to Star64 JH7110
And printed to the Serial Console in RISC-V Assembly
We fooled U-Boot Bootloader into thinking we’re Linux
But we demoted NuttX to Supervisor Privilege Level
Helped by OpenSBI Supervisor Interface
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
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…
Register A0: Hart ID
Register A1: RAM Address of Device Tree
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…
Hart ID is 1 (Register A0)
RAM Address of Device Tree is 0x4600
0000
(Register A1)
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)
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)…
Hart 0: S7 Monitor Core (RV64IMACB)
Harts 1 to 4: U74 Application Cores (RV64GCB)
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
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 */
Earlier we identified these fixes for the NuttX Boot Code to run in Supervisor Mode…
Remove mhartid
because OpenSBI will pass Hart ID in Register A0
Subtract 1 from Register A0 because NuttX expects Hart ID to start with 0
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
(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
If Hart ID is 1, 2, 3, …
g_cpu_basestack
and Hart ID1:
/* 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
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 */
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)
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
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.
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:[6CArmbian
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 ###