Skip to content

risc-v/gd32vw55x: add GigaDevice GD32VW553 port (Wi-Fi 6)#19445

Merged
acassis merged 1 commit into
apache:masterfrom
JorgeGzm:gd32vw553-port
Jul 16, 2026
Merged

risc-v/gd32vw55x: add GigaDevice GD32VW553 port (Wi-Fi 6)#19445
acassis merged 1 commit into
apache:masterfrom
JorgeGzm:gd32vw553-port

Conversation

@JorgeGzm

@JorgeGzm JorgeGzm commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

New RISC-V chip port for the GigaDevice GD32VW55x (Nuclei N307, Wi-Fi 6 + BLE 5.3) and the GD32VW553K-START board. The radio comes from the prebuilt vendor libraries behind a thin sys_* facade; the SDK is cloned and pinned at build time (same pattern as esp-hal-3rdparty). Everything that touches hardware registers is a native NuttX driver.

HAL drivers: serial, DMA, GPIO/EXTI, SPI, I2C, ADC, PWM, input capture, FWDGT and WWDGT watchdogs, PROGMEM (internal flash via the mask ROM API), TRNG, CRC, RTC, plus board LEDs and software reset. Wi-Fi (wlan0, station and softAP) is a netdev_lowerhalf driver on the NuttX network stack. BLE 5.3 is present but EXPERIMENTAL and off by default.

Full details are in the new documentation under Documentation/platforms/risc-v/gd32vw55x/.

gd32vw553k-start

Impact

New arch/chip and board; no existing code is changed. The Wi-Fi/BLE configurations need the vendor SDK (fetched at build time); nsh and periph build without it.

Testing

Build host: Linux, riscv-none-elf-gcc 14.2.0.
All seven configurations build, nxstyle is clean, and the docs build without
warnings. nsh and periph build without the vendor SDK; the Wi-Fi/BLE
configurations fetch it at build time.

Build and flash (per configuration):

./tools/configure.sh gd32vw553k-start:<nsh|periph|littlefs|wapi|sta_softap|ble|ostest>
make -j
openocd -f openocd_gdlink.cfg \
        -c "program nuttx.bin 0x08000000 verify reset exit"

Validated on hardware (GD32VW553K-START, console on the GD-Link VCP,
115200 8N1). Verbatim console output below; MAC/BSSID addresses are the only
redaction (XX:XX:XX:XX:XX:XX), and the Wi-Fi passphrase is masked.

nsh

NuttShell (NSH) NuttX-13.0.0
nsh> uname -a
NuttX 13.0.0 52c5942112 Jul 15 2026 23:23:35 risc-v gd32vw553k-start
nsh> free
      total       used       free    maxused    maxfree  nused  nfree name
     284032       6232     277800       7496     276904     22      2 Umem
nsh> ps
  TID   PID  PPID PRI POLICY   TYPE    NPX STATE    EVENT     SIGMASK            STACK    USED FILLED COMMAND
    0     0     0   0 FIFO     Kthread   - Ready              0000000000000000 0002016 0000364  18.0%  Idle_Task
    2     2     0 100 RR       Task      - Running            0000000000000000 0003008 0001848  61.4%  nsh_main

periph

nsh> ls /dev
/dev:
 adc0
 capture0
 console
 i2c0
 null
 pwm0
 random
 spi0
 ttyS0
 userleds
 watchdog0
 watchdog1
 zero
nsh> dd if=/dev/random of=/dev/null bs=8 count=1
8 bytes (1 blocks) copied, 0 usec, 4294967295 KB/s
nsh> leds
leds_main: Starting the led_daemon
leds_main: led_daemon started
led_daemon (pid# 5): Running
led_daemon: Opening /dev/userleds
led_daemon: Supported LEDs 0x07
led_daemon: LED set 0x01
led_daemon: LED set 0x02
led_daemon: LED set 0x03
led_daemon: LED set 0x04
led_daemon: LED set 0x05
led_daemon: LED set 0x06
led_daemon: LED set 0x07

littlefs

LittleFS is auto-mounted at /data; a file is written, read back, and
survives a reboot.

nsh> mount
  /data type littlefs
  /proc type procfs
nsh> echo nuttx-gd32-lfs-ok > /data/persist.txt
nsh> cat /data/persist.txt
nuttx-gd32-lfs-ok
nsh> ls -l /data
/data:
 drwxrwxrwx           0 .
 dr--r--r--           0 ..
 -rw-rw-rw-          16 2026-07-13 00:00 final.txt
 -rw-rw-rw-          19 2026-07-13 00:00 hello.txt
 -rw-rw-rw-          18 2026-07-13 00:00 persist.txt
 -rw-rw-rw-          18 2026-07-13 00:00 teste.txt
nsh> reboot

NuttShell (NSH) NuttX-13.0.0
nsh> cat /data/persist.txt
nuttx-gd32-lfs-ok

wapi - Wi-Fi station

nsh> ifup wlan0
ifup wlan0...OK
nsh> wapi scan wlan0
bssid / frequency / signal level / encode / ssid
XX:XX:XX:XX:XX:XX	2462	-78	0800	Claro_T1153
XX:XX:XX:XX:XX:XX	2462	-75	0800	4M162
XX:XX:XX:XX:XX:XX	2462	-85	0800	Shibia
XX:XX:XX:XX:XX:XX	2462	-78	0800	Claro_T1153-IoT
XX:XX:XX:XX:XX:XX	2462	-86	0800	Dian Carlos
XX:XX:XX:XX:XX:XX	2462	-87	0800	Ap SHELBY
XX:XX:XX:XX:XX:XX	2462	-87	0800	Guarani_2G
XX:XX:XX:XX:XX:XX	2452	-80	0800	#CLARO-WIFI
XX:XX:XX:XX:XX:XX	2452	-82	0800	NET_2GC48A72
XX:XX:XX:XX:XX:XX	2447	-76	0800	Daniekel
XX:XX:XX:XX:XX:XX	2437	-77	0800	Ap 141
XX:XX:XX:XX:XX:XX	2437	-84	0800	RICARDO
XX:XX:XX:XX:XX:XX	2437	-59	0800	Dunder Mifflin Paper Co
XX:XX:XX:XX:XX:XX	2432	-54	0800	Miguel
XX:XX:XX:XX:XX:XX	2422	-89	0800	McFly2.4G
XX:XX:XX:XX:XX:XX	2412	-24	0800	@GUZM
nsh> wapi mode  wlan0 2
nsh> wapi psk   wlan0 ******** 3
nsh> wapi essid wlan0 @GUZM 1
[0] (-26 dBm) CH=  1 BSSID=XX:XX:XX:XX:XX:XX SSID=@GUZM  [RSN:WPA-PSK CCMP/CCMP]
MAC: auth req send
MAC: auth rsp received, status = 0
MAC: assoc req send
MAC: assoc rsp received, status = 0
WPA: 4-1 received
WPA: 4-2 send
WPA: 4-3 received
WPA: 4-4 send
WIFI_MGMT: IPv4 addr got 10.0.0.2
nsh> renew wlan0
nsh> ifconfig wlan0
wlan0	Link encap:Ethernet HWaddr XX:XX:XX:XX:XX:XX at RUNNING mtu 1500
	inet addr:192.168.15.7 DRaddr:192.168.15.1 Mask:255.255.255.0
nsh> ping -c 3 8.8.8.8
PING 8.8.8.8 56 bytes of data
56 bytes from 8.8.8.8: icmp_seq=0 time=27.0 ms
56 bytes from 8.8.8.8: icmp_seq=1 time=10.0 ms
56 bytes from 8.8.8.8: icmp_seq=2 time=8.0 ms
3 packets transmitted, 3 received, 0% packet loss, time 3004 ms
rtt min/avg/max/mdev = 8.000/15.000/27.000/8.524 ms

ifup wlan0 must be issued before the wapi commands (standard NuttX flow);
without it the interface is not IFF_UP, the netdev upper half skips the TX,
and dhcpc blocks. The port is WPA2-only; associating to a WPA3 (SAE) AP
faults in the vendor supplicant (tracked as a follow-up).

sta_softap - Wi-Fi softAP

Board side: the board becomes the nuttxwifi AP at 10.0.0.1 and starts a DHCP
server.

gdwifi: rf_power_on
=== RF initialization finished ===
=== WiFi calibration done ===
=== PHY initialization finished ===

NuttShell (NSH) NuttX-13.0.0
nsh> ifconfig wlan0 10.0.0.1
nsh> wapi mode  wlan0 3
nsh> wapi psk   wlan0 12345678 3
nsh> wapi essid wlan0 nuttxwifi 1
IP: 192.168.237.1/24
GW: 192.168.237.1/24
nsh> ifconfig wlan0
wlan0	Link encap:Ethernet HWaddr XX:XX:XX:XX:XX:XX at RUNNING mtu 1500
	inet addr:10.0.0.1 DRaddr:10.0.0.1 Mask:255.255.255.0
nsh> dhcpd_start wlan0

A laptop joins nuttxwifi and the board's DHCP server leases it 10.0.0.2:

$ nmcli dev wifi connect nuttxwifi password 12345678
Device 'wlp0s20f3' successfully activated
$ ip -4 addr show wlp0s20f3
    inet 10.0.0.2/24 brd 10.0.0.255 scope global dynamic wlp0s20f3

The board pings the leased client:

nsh> ping -c 3 10.0.0.2
PING 10.0.0.2 56 bytes of data
56 bytes from 10.0.0.2: icmp_seq=0 time=111.0 ms
56 bytes from 10.0.0.2: icmp_seq=1 time=30.0 ms
56 bytes from 10.0.0.2: icmp_seq=2 time=52.0 ms
3 packets transmitted, 3 received, 0% packet loss, time 3004 ms
rtt min/avg/max/mdev = 30.000/64.333/111.000/34.199 ms

ble (EXPERIMENTAL)

wapi plus BLE and the demo GATT service. The board advertises NuttX and
registers a "transparent UART" service (16-bit UUIDs 0xffe0 / RX 0xffe1 /
TX 0xffe2). A central connects, discovers the service, and a write to the RX
characteristic is received on the board console.

Host side (bleak):

[scan] found XX:XX:XX:XX:XX:XX 'NuttX'
[conn] connected
[gatt] service 0000ffe0-0000-1000-8000-00805f9b34fb
[gatt]   char 0000ffe1-0000-1000-8000-00805f9b34fb ['write-without-response', 'write']
[gatt]   char 0000ffe2-0000-1000-8000-00805f9b34fb ['notify']
[write] RX <- Hello world
[write] RX <- NuttX GD32VW55x
[done] disconnecting

Board console:

NuttShell (NSH) NuttX-13.0.0
nsh> BLE RX (11): Hello world
BLE RX (15): NuttX GD32VW55x

The central -> board write uses a write command (write-without-response),
which is reliable. The prebuilt vendor controller does not complete a
write-request or the CCCD subscribe a Linux BlueZ host issues, so the TX
notification echo (board -> central) is best exercised from a phone app such
as nRF Connect. This is why BLE keeps CONFIG_EXPERIMENTAL.

ostest

The OS test suite runs to completion (every sub-test reports nerrors=0 and it
ends with ostest_main: Exiting with status 0). The rr_test prime workload
makes the full run take a couple of minutes on this core.

nsh> ostest
...
mutex_test: PASSED
...
robust_test: Test complete with nerrors=0
...
user_main: Exiting
ostest_main: Exiting with status 0

@github-actions github-actions Bot added Area: Build system Arch: risc-v Issues related to the RISC-V (32-bit or 64-bit) architecture Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. Board: risc-v labels Jul 15, 2026
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@linguini1 linguini1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please attach some logs from your testing of the WiFi, BLE and other peripherals? And an OSTest log for the new board?

acassis
acassis previously approved these changes Jul 15, 2026

@acassis acassis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JorgeGzm please include to the commit message:
Assisted-by: Claude (Anthropic)

@JorgeGzm
JorgeGzm marked this pull request as draft July 15, 2026 21:06
@JorgeGzm

Copy link
Copy Markdown
Contributor Author

I made a rebase with the master and the wifi and ble broke, i revising all.

The intention is to submit this PR is send a a functional and fully tested version.

@JorgeGzm
JorgeGzm dismissed stale reviews from acassis and xiaoxiang781216 via 52c5942 July 16, 2026 02:15
@JorgeGzm
JorgeGzm marked this pull request as ready for review July 16, 2026 03:07
@JorgeGzm

Copy link
Copy Markdown
Contributor Author

All tested and fixed!!

New RISC-V chip port for the GigaDevice GD32VW55x (Nuclei N307, Wi-Fi 6
and BLE 5.3 combo), with the GD32VW553K-START board.

Core:
- ECLIC interrupt controller, clock tree (160 MHz / 40 MHz HXTAL),
  64-bit machine timer, serial (USART0/UART1/UART2) and RTC.
- The instruction cache must be enabled in head.S or the UART drops
  characters under load; the mask ROM uses the first 0x200 bytes of SRAM
  so the image is linked at 0x20000200; the RTC needs PMU_CTL0.BKPWEN or
  the register writes are silently discarded.
- The ECLIC only auto-clears the interrupt pending bit in hardware-vectored
  mode, so the trap dispatch clears it for edge-triggered sources or they
  re-fire forever (level-triggered peripherals clear their own).

Peripherals: DMA (8 channels), GPIO + EXTI, SPI (optional DMA), I2C0/I2C1
(the new STM32-v2-style IP, not the GD32F4 one), ADC, PWM and input
capture on the timers, both watchdogs, PROGMEM, TRNG and CRC.  All use
the standard NuttX lower-half interfaces and direct register access, not
the vendor SPL.  Three user LEDs (GPIOC), and a software reset through the
Nuclei SysTimer.

Internal flash: it is a system-in-package NOR die behind the real-time
decryption block, so it is not programmed through the FMC registers -- the
driver goes through the mask ROM API (rom_flash_*), the same way the
vendor code does.  PROGMEM can be mounted as LittleFS; the region sits
below the Wi-Fi NVDS and must not overlap it.

Wi-Fi (wlan0), station and softAP:
- The MAC/PHY, RF and WPA supplicant are linked as prebuilt BSD-3
  libraries.  They are RTOS-agnostic, so the OS binding is a sys_* facade
  on NuttX primitives (gdwifi/wrapper_nuttx.c).  Critical sections mask by
  ECLIC priority threshold, not by disabling every interrupt, or the MAC
  misses its microsecond deadlines.
- The lwIP stack of the SDK is not used: the interface is a
  netdev_lowerhalf driver driven by the standard tools (wapi, ifup, renew,
  ping, dhcpd).  EAPOL is handed to the supplicant instead of the IP
  stack.  The lowerhalf "priority" field is the work-queue id (HPWORK/
  LPWORK), not a task priority -- a wrong value makes receive() never run
  and every packet is dropped.
- The vendor gives the radio 32 KB of shared SRAM as an extra heap; this
  needs CONFIG_MM_REGIONS >= 2 or the kernel silently drops the region.
- The DHCP client needs CONFIG_NETUTILS_DHCPC_BOOTP_FLAGS=0x8000 so the
  server answers the OFFER by broadcast.
- softAP (wapi master mode -> wifi_management_ap_start): the single-VIF
  firmware does station or AP at a time.  Use WPA2, not WPA3: the SAE
  handshake is deep on the stack and overflows the elliptic-curve crypto
  with the default task stacks, so the Wi-Fi configs raise
  CONFIG_INIT_STACKSIZE/DEFAULT_TASK_STACKSIZE.

Vendor SDK: cloned by the build at "context" time, pinned to a validated
commit and patched in place -- so CI can build with nothing preinstalled,
the same pattern as esp-hal-3rdparty.  The prebuilt libraries are built
for the hard-float ilp32f ABI.

BLE 5.3, marked EXPERIMENTAL and off by default: the prebuilt libble is an
all-in-one controller plus RivieraWaves host (GAP/GATT/SMP inside the blob)
with no HCI transport, so the port drives the vendor host directly
(ble_adp_*, ble_adv_*, ble_gap_*) instead of registering a bt_driver_s.
The SDK's radio interrupt handlers must be attached to the NuttX IRQ table
(they are reached through the ECLIC hardware vector table in the vendor
build); attaching also keeps --gc-sections from dropping them.  It ships
with no configuration; enabling it advertises as "NuttX", connectable.

Boards: gd32vw553k-start with nsh, wapi, sta_softap, periph and littlefs
configurations, and the board added to the CI build list.

Tested on hardware (GD32VW553K-START): NSH, the peripheral drivers
(TRNG entropy and the LEDs exercised), LittleFS (write, read, survives a
reboot), the full Wi-Fi station path (scan, WPA2, DHCP, ping to the
internet), the softAP (a client associates with WPA2, gets an address
from the board's DHCP server, and pings the board), and BLE (advertises as
"NuttX"; a central connects and enumerates the GATT database).

Assisted-by: Claude Opus 4.8
Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
@acassis
acassis dismissed linguini1’s stale review July 16, 2026 13:03

Test logs were added!

@acassis
acassis merged commit 26ec938 into apache:master Jul 16, 2026
37 of 54 checks passed
@JorgeGzm
JorgeGzm deleted the gd32vw553-port branch July 16, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: risc-v Issues related to the RISC-V (32-bit or 64-bit) architecture Area: Build system Board: risc-v Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants