Skip to content

Building and running

This page explains how to build a Zephyr program and get it running on the YuzukiNeko in three ways:

MethodWhere the program livesAfter power-offRequiresSuited to
Method 1: download to RAM and runPSRAMLostFEL + xfelDevelopment and debugging; build once, run once
Method 2: write NOR directly with xfelSPI NORRetained, runs automatically on power-upA loader in NOR; enter FEL + xfel each timeOne fixed program, without installing a UF2 downloader
Method 3: UF2 drag-and-drop downloadSPI NORRetained, runs automatically on power-upA loader and the UF2 downloader in NOR; only a USB cableThe general everyday download method

All three methods use the same zephyr.bin; there is no need to build separately for each. Methods 2 and 3 both require the loader to be installed in NOR first, see The common basis for NOR boot.

Before you start, complete Configuring the development environment and Getting the Zephyr source, and install xfel (see FEL download mode and tools).

Terminal window
. ~/f101-zephyr/env.sh
cd ~/f101-zephyr
west build -b yuzukineko -d build/hello zephyr/samples/hello_world
ArgumentMeaning
-b yuzukinekoBoard. Use f101_evb for the evaluation board
-d build/helloBuild directory; use a different directory for each program so they do not overwrite each other
zephyr/samples/hello_worldThe application to build; replace it with the path of another sample

The output is under build/hello/zephyr/:

FileDescription
zephyr.binBare binary; this is what gets downloaded to the board
zephyr.elfELF with symbols, for debugging and inspecting the memory layout

The image’s link address and entry point are both 0x40000000 (near the start of PSRAM). The toolchain is the XuanTie 900 newlib GCC, wired in through ZEPHYR_TOOLCHAIN_VARIANT=cross-compile, set by env.sh.

To rebuild the same directory, use west build -d build/hello; if you changed the board or want to start over, add -p always.

The console of yuzukineko is UART1, PB00 (TX) / PB01 (RX), 115200 8N1. Connect the USB-to-serial adapter’s RX to PB00 and its TX to PB01, share ground, then open any serial terminal, for example:

Terminal window
picocom -b 115200 /dev/ttyUSB0

When there is no output, first work out which link is at fault: the wiring and terminal parameters, whether the program is really executing, or Zephyr itself. A useful approach is to first load a small bare-metal program (linked at 0x40000000, polling UART and writing the transmit register) to prove that this path works.

The host writes zephyr.bin into PSRAM over USB and then jumps to it, without touching Flash.

  1. Put the board into FEL: hold the FEL button, connect the USB data cable, release (see FEL download mode and tools).

  2. Confirm the connection:

    Terminal window
    xfel version

    The output should contain AWUSBFEX ID=0x00193700(F101).

  3. Initialize memory, load, and run:

    Terminal window
    xfel ddr f101-s3
    xfel write 0x40000000 build/hello/zephyr/zephyr.bin
    xfel exec 0x40000000

At this point the console should start producing output. hello_world prints Hello World! yuzukineko/sun252i_f101.

CommandEffect
xfel ddr f101-s3Initialize PSRAM. This helper also sets up the UART clock and pin multiplexing; without it the console has no output
xfel write 0x40000000 …Write the image to the link address
xfel exec 0x40000000Jump to the entry point and start running

Points to note:

  • Every download needs a fresh power-up into FEL. After exec the CPU has left the BROM and FEL is no longer available.
  • This path skips boot0 and u-boot, so the early SoC initialization (CPU CSR, console clock) is handled by Zephyr itself.
  • Do not use xfel read32 or hexdump to check status after exec; the USB link is already gone. Status can only be checked before exec.
  • Seeing usb bulk send/recv error after exec means the CPU is already running your code, which is normal, but it does not mean Zephyr itself is running correctly; go by the console output.

Methods 2 and 3 both write the program into the onboard 16 MiB SPI NOR, and a loader in the Flash fetches it out and runs it at power-up. This section explains how that works and how to install the loader.

ContentNOR offsetDescription
Loader (three copies)0x000000 / 0x010000 / 0x020000SyterKit flash-boot
Saved Flash sample point0x02f000Maintained by the loader itself, do not overwrite
UF2 downloader (system program)0x030000Header page + image, up to 0x1CF000 bytes; needed only by method 3
Application (your Zephyr program)0x200000Header page + image, up to 0x9F0000 bytes

At power-up the BROM reads the loader into SRAM, the loader initializes PSRAM and the Flash controller, then chooses one of two paths:

  • The button on PD5 is not held, and the application header page is valid: copy the application to PSRAM and run it.
  • Otherwise: run the UF2 downloader, and the board appears on the USB port as the drive F101BOOT, waiting for a .uf2 to be dragged in.

The application runs in PSRAM just as in method 1, not in place from Flash, so it is the same zephyr.bin. Each program is preceded by a 4 KiB header page that records the PSRAM address the image loads to (the default 0x40000000) and its size. The header page is added automatically by mkuf2.py; you do not write it by hand.

mkuf2.py lives in the zephyr-components repository; it is a Python script that only needs the standard library, with no extra packages to install. For what the repository is for, see Get zephyr-components; if you have already cloned it, skip the git clone below:

Terminal window
cd ~/f101-zephyr
git clone https://github.com/YuzukiHD/zephyr-components.git
# By default use the latest main. The line below pins the older commit that the steps of
# this page were first verified on (2026-10-10); it predates gbsp/ and nekobox/, so leave it
# commented out unless you need exactly that version.
# git -C zephyr-components checkout 282361ce5883bd65c228da584758e4796bf439e7
mkdir -p out
Terminal window
cd ~/f101-zephyr
git clone https://github.com/YuzukiHD/SyterKit.git
cd SyterKit
git checkout 6ef3f27abd547439cb2de4e455e40a2cbfbebff8
export CROSS=$HOME/f101-zephyr/toolchains/Xuantie-900-gcc-elf-newlib-x86_64-V3.2.0/bin/riscv64-unknown-elf-
make O=../out/syterkit CROSS_COMPILE=$CROSS yuzukineko_rv32_sram_defconfig
make O=../out/syterkit CROSS_COMPILE=$CROSS olddefconfig
make -j8 O=../out/syterkit CROSS_COMPILE=$CROSS flash-boot
cd ..

The output is out/syterkit/build/yuzukineko/app_sram/flash-boot/flash-boot_spi.bin (32 KiB).

Enter FEL, back up the whole Flash first, then write the loader three times:

Terminal window
xfel version
xfel spinor read 0x0 0x1000000 neko-backup.bin
L=out/syterkit/build/yuzukineko/app_sram/flash-boot/flash-boot_spi.bin
xfel spinor write 0x000000 $L
xfel spinor write 0x010000 $L
xfel spinor write 0x020000 $L

xfel spinor write erases the sectors it needs automatically. Writing Flash does not require running xfel ddr.

Then continue according to the method you are using: method 2 writes the application, method 3 writes the UF2 downloader.

mkuf2.py adds a header page to zephyr.bin. --raw outputs the raw Flash image, for use with xfel spinor write; without --raw it outputs a .uf2 file.

Terminal window
python3 zephyr-components/uf2-downloader/tools/mkuf2.py app build/hello/zephyr/zephyr.bin --raw -o out/app.img

The output is similar to out/app.img: 2096208 bytes at 0x200000. When the image is larger than the application area (0x9F0000 bytes) the script reports an error and exits. The default load address is 0x40000000, matching Zephyr’s link address; when the image is linked elsewhere, specify it with --load.

Without installing the UF2 downloader, use xfel each time to write the application to 0x200000 in NOR. Suited to the case where you only want one fixed program to run.

Prerequisite: the loader has been written into NOR as described under The common basis for NOR boot above.

1. Generate the application image

Terminal window
python3 zephyr-components/uf2-downloader/tools/mkuf2.py app build/hello/zephyr/zephyr.bin --raw -o out/app.img

2. Enter FEL and write

Hold the FEL button while powering up, then:

Terminal window
xfel version
xfel spinor write 0x200000 out/app.img
xfel reset

3. Watch the console

The loader prints its own log first, then runs your program:

PSRAM: … MiB initialized
SPI NOR: flash id 0x…, … KiB
SPI NOR: image 0x00201000 -> 0x40000000 (… KiB)
starting at 0x40000000 (id 0)

Afterwards, for each code change: rebuild → mkuf2.py app … --raw → hold the FEL button while powering up → xfel spinor write 0x200000 → xfel reset.

This is the general everyday download method. xfel is used only once at the very beginning, to install the UF2 downloader into NOR; afterwards you turn the application into a .uf2 file and drag it into the USB drive the board shows, and FEL and xfel are no longer needed.

Step one: install the UF2 downloader (once only)

Section titled “Step one: install the UF2 downloader (once only)”

Prerequisite: the loader has been written into NOR as described under The common basis for NOR boot.

1. Build the UF2 downloader and add the header page

Terminal window
west build -b yuzukineko -d build/uf2 zephyr-components/uf2-downloader
python3 zephyr-components/uf2-downloader/tools/mkuf2.py system build/uf2/zephyr/zephyr.bin --raw -o out/sys.img

2. Enter FEL and write

Hold the FEL button while powering up, then:

Terminal window
xfel version
xfel spinor write 0x030000 out/sys.img
xfel reset

Here you do not write the application. When there is no valid application in Flash, the loader runs the downloader directly.

3. Confirm the UF2 drive appears

After the reset, connect to the computer with a USB data cable; the board should appear as the USB drive F101BOOT. Seeing this drive means the installation is complete.

Step two: download an application with UF2 (the everyday flow)

Section titled “Step two: download an application with UF2 (the everyday flow)”

Turn zephyr.bin into a .uf2:

Terminal window
python3 zephyr-components/uf2-downloader/tools/mkuf2.py app build/hello/zephyr/zephyr.bin -o out/app.uf2

The output is similar to out/app.uf2: 2096208 bytes at 0x200000, 8189 UF2 blocks. The .uf2 file is about twice the size of zephyr.bin, because each block carries only 256 bytes of data.

Then:

  1. Put the board into the UF2 downloader: hold the button on PD5, then power up (or reset). When there is no valid application in Flash the button is not needed, it enters automatically.
  2. The board appears as the F101BOOT drive.
  3. Copy out/app.uf2 into this drive.
  4. After the copy finishes the board restarts automatically, the loader runs the new application, and the console starts producing output.

Afterwards, for each code change: rebuild → mkuf2.py app … → hold the button while powering up → copy.

File typeCommandWrite locationRange it can write
Applicationmkuf2.py app zephyr.bin -o x.uf20x200000Only the application area
The downloader itselfmkuf2.py system zephyr.bin -o x.uf20x030000Any location
Loadermkuf2.py loader flash-boot_spi.bin -o x.uf20x000000Any location

For more detail about UF2 see UF2 drag-and-drop flashing.

Method 1: the console has no output after exec: first confirm the console wiring and parameters, and that xfel ddr f101-s3 has been run. If there is still nothing, use the small bare-metal program mentioned in the “Console” section above to work out which link is at fault.

mkuf2.py reports … bytes, the limit is 10420224: the application exceeds the 0x9F0000-byte application area. Make the program smaller, or drop features you do not need.

The F101BOOT drive does not appear: confirm you are using a USB-C cable with data lines, plugged into the download port; confirm you have held the button on PD5 before powering up. If it still does not appear, the loader or the downloader is not installed properly; hold the FEL button while powering up and rewrite them.

The board restarts after a copy or write but has no output: first use method 1 to load the same zephyr.bin into memory and run it, to see whether the program itself is fine, then investigate the image in NOR.

The board does nothing at all at power-up: the loader has been overwritten or written incorrectly. Hold the FEL button while powering up and rewrite the loader, or restore from the backup: xfel spinor write 0x0 neko-backup.bin.