This is the Enhanced build of the A2FPGA Apple II card for the Tang Nano 20K. It pairs the Tang Nano 20K's Gowin GW2AR-18 FPGA (8 MB SDRAM) with the on-board BL616 MCU acting as a coprocessor: the MCU is a USB host (game controller, mass storage, Ethernet), serves Apple II disk images from a USB stick or SD card, drives an on-screen menu system, and bridges networking — no PC required once the board is flashed.
The A2N20v2 card supports Apple II, //e, and IIgs models (the "a2bridge" CPLD captures all bus signals including M2SEL/M2B0 for the IIgs).
Status: beta. This document is the end-to-end guide for beta testers: what you need, how to flash both the FPGA and the MCU, how to prepare a USB stick, and what to expect when it boots.
Virtual peripheral cards (default slot assignments; changeable in the menu):
| Slot | Card | Notes |
|---|---|---|
| 2 | Super Serial Card | |
| 3 | Uthernet II (WIZnet W5100) | Ethernet, bridged through a USB-Ethernet adapter on the MCU — works with IP65, ADTPro, Contiki |
| 4 | Mockingboard | |
| 5 | Disk II controller | Two floppy drives served from disk images |
| 6 | ProDOS hard disk | Two block-device units served from disk images; bootable |
| 7 | SuperSprite (TMS9918A/F18A) |
Plus: HDMI video output for all Apple II/e/gs graphics modes, Ensoniq DOC 5503 sound (IIgs audio, 32 oscillators), Apple II speaker over HDMI, and a gamepad-driven on-screen menu for configuration.
Floppy drives (Disk II, slot 5) serve these image formats, read and write:
| Format | Contents | Notes |
|---|---|---|
.dsk / .do |
143,360-byte sector image | Sector order is auto-detected from content (DOS 3.3 vs ProDOS-order images both work) |
.po |
143,360-byte sector image | ProDOS sector order |
.2mg |
2IMG container | Floppy-size payloads serve as floppies |
.nib |
232,960-byte nibble image | Served as-is |
Hard disk units (slot 6) serve ProDOS block volumes, read and write:
| Format | Notes |
|---|---|
.hdv |
Raw ProDOS blocks, up to 32 MB (65,535 blocks) |
.po |
Any size — raw blocks |
.2mg |
ProDOS-order payloads |
Tracks/blocks are served on demand by the MCU — images stay on the USB stick / SD card and are not size-limited by FPGA memory.
- A2N20v2 card with Tang Nano 20K (Enhanced build targets the fused BL616 boards Sipeed currently ships)
- USB-C cable to the Tang Nano 20K's Debug port (the USB-C port on the BL616 side) for flashing
- A small USB-C to USB-A hub (the BL616 has one USB host port; you'll want at least a gamepad + storage on it)
- XInput game controller — 8BitDo SN30 Pro or similar (the menu's only input device; no Apple II keyboard needed)
- USB flash drive, FAT32-formatted (MBR partition scheme) — or a FAT32 SD card in the Tang Nano 20K's SD slot
- Optional: USB 2.0 10/100 Mbps Ethernet adapter (Realtek RTL8152) for the Uthernet II bridge
- A Mac or PC with:
- openFPGALoader — on a
Mac:
brew install openfpgaloader - Python 3 with
pip install bflb-iot-tool(for MCU flashing)
- openFPGALoader — on a
Mac:
There are two things to flash, and they are flashed differently:
- the FPGA bitstream (
.fsfile — the Apple II hardware itself) - the BL616 MCU firmware (
.binfile — USB host, disks, menu)
Flash the FPGA first, then the MCU. Both are done over the same Debug USB-C port.
The PC procedures below are only needed for the FIRST flash (or for recovery). Once the board runs this firmware, both the MCU firmware and the FPGA core can be updated from the USB stick through the on-screen menu — no PC, no cables (see "Updating … from the USB stick" below).
Connect the Debug port to your computer and power the board normally (no buttons). The BL616's stock bootloader detects the attached computer and exposes a JTAG programmer (the host firmware stays dormant while a computer is attached, so this works even with our MCU firmware installed).
openFPGALoader -b tangnano20k -f boards/a2n20v2-Enhanced/impl/pnr/a2n20v2_enhanced.fs
or, from the repository root:
tools/flash.sh a2n20v2-Enhanced
This writes the bitstream to the FPGA's SPI flash (-f), so it persists
across power cycles. The MCU firmware is untouched.
⚠️ Never usemake flash,BLFlashCommand, or any tool that writes to flash address0x0. Sipeed's boards chain-boot from an encrypted first-stage bootloader at0x0; overwriting it disables the board's USB JTAG/serial functions until the stock firmware is restored. Our firmware lives at0x40000(Stage 2) and is flashed only with thea2n20-mcu-programtool below, which enforces the correct address.
Step 1 — put the board in ROM boot mode (required; the running firmware cannot reflash itself):
- Disconnect the USB cable from the board
- Press and hold the UPDATE button (recessed, behind the HDMI connector)
- Plug in the USB-C cable (Debug port) while still holding UPDATE
- Release UPDATE
The board enumerates as a Bouffalo serial device — on a Mac it appears as
/dev/cu.usbmodemXXXX (if you instead see two usbserial-… ports, the
board booted normally; redo the button sequence).
Step 2 — flash (from boards/a2n20v2-Enhanced/src/a2n20_bl616/):
./tools/a2n20-mcu-program --stage2 \
--firmware firmware_host/build/build_out/a2n20_bl616_host_bl616.bin \
--port /dev/cu.usbmodemXXXX --non-interactive --verify-flash
Wait for both [OK] Stage 2 flashed successfully and
[OK] verified … bytes at 0x40000 match the image. The --verify-flash
read-back is your proof it worked — the host firmware is invisible over USB
once running, so there's no other way to confirm from the PC side.
If the board does nothing after flashing Stage 2 (power-cycles back
into the Bouffalo boot device, no FPGA activity): your board's first-stage
bootloader at 0x0 is missing, damaged, or is an early Sipeed release
that cannot chain-load firmware from 0x40000. Fix it by restoring the
correct Stage 1 — from boot mode:
./tools/a2n20-mcu-program --stage1 --port /dev/cu.usbmodemXXXX --non-interactive --verify-flash
With no path, --stage1 reads the chip's eFuse security state from the
BootROM and picks the right image automatically: fused boards (most
retail Tang Nano 20Ks — the BootROM requires an AES-encrypted image) get
Sipeed's bl616_fpga_partner_20kNano.bin, unfused boards (which
reject that encrypted image) get the plaintext friend_20k_bl616.bin.
Then flash Stage 2 as above. When reporting problems, include the
eFuse state: … boot_info=… line the tool prints — it tells us exactly
what kind of board you have.
Step 3 — run it: disconnect the board from the computer entirely and power it in the Apple II with no PC attached. The MCU firmware only starts when no computer is enumerating the BL616 (that's what lets step 1 of the FPGA flash work).
Once your board runs a firmware with the on-screen menu, later MCU updates don't need a PC at all:
- Copy the new
a2n20_bl616_host_bl616.binanywhere on the USB stick - Menu → FIRMWARE UPDATE → CHOOSE FIRMWARE FILE (.BIN) and pick it
- The update is staged and verified in the background (disks keep working); when it shows READY, choose INSTALL NOW
- The screen freezes for about a minute while the update is written — do not power off during this
- The board restarts itself when the install finishes and the new firmware boots (verify the build stamp on the FIRMWARE UPDATE screen). If it hasn't come back after two minutes, power-cycle the system.
The staging step is fully verified before anything is overwritten, so a bad file or interrupted copy cannot hurt the installed firmware. Only the short install window is critical; if power is lost there, recover with the PC flashing procedure above (the UPDATE-button boot mode always works).
The FPGA core (gateware) can also be updated from the stick — the BL616 writes the bitstream into the FPGA's configuration flash over JTAG:
- Copy the build's
impl/pnr/a2n20v2_enhanced.binanywhere on the stick (the raw.bin, not.fsor.binx) - Menu → FPGA UPDATE → CHOOSE CORE FILE (.BIN) and pick it
- The file is verified first (Gowin bitstream for this exact FPGA; an MCU
firmware
.binis rejected), then choose INSTALL NOW - The screen goes completely dark for one to two minutes while the core is written — this is normal; do not power off
- The board restarts itself into the new core; the main menu shows the new core build stamp next to the MCU one
Unlike the MCU update there is no staged copy: the running core must be
stopped before its flash is reachable. If the write is interrupted, the
FPGA comes up unconfigured (dark screen) but the MCU stays fully alive —
recover by re-flashing the FPGA from a PC (tools/flash.sh a2n20v2-Enhanced with the board attached to the computer).
Nothing here can permanently brick the board: the UPDATE-button boot mode
is in mask ROM. If the MCU firmware misbehaves, redo boot mode and reflash
(our firmware, or Sipeed's stock bl616_fpga_partner at its documented
address — see the BL616 firmware README).
Format: FAT32, MBR partition scheme (on a Mac: Disk Utility → Erase → "MS-DOS (FAT)" + "Master Boot Record"). Then copy disk images to the root.
At startup the firmware mounts, per drive, the first name that exists:
| Drive | Names tried, in order |
|---|---|
| Floppy 1 | disk1.dsk, disk1.do, disk1.po, disk1.2mg, disk1.nib |
| Floppy 2 | disk2.dsk, disk2.do, disk2.po, disk2.2mg, disk2.nib |
| Hard disk 1 | hdd1.hdv, hdd1.po, hdd1.2mg |
| Hard disk 2 | hdd2.hdv, hdd2.po, hdd2.2mg |
Images with any other name — including in subdirectories — can be selected from the on-screen menu (DISK IMAGES → pick a drive → browse folders and choose); the choice is saved and survives power cycles. A USB stick takes priority over the SD card by default (configurable in STORAGE).
A good starter set: a DOS 3.3 disk as disk1.dsk, a blank 143,360-byte
file as disk2.dsk, and a bootable ProDOS volume (e.g. Total Replay) as
hdd1.hdv.
- The Apple II stays quiet for a few seconds. The FPGA holds the
Apple II in RESET while the MCU brings up USB and mounts images, so the
autoboot scan doesn't run before storage is ready. The HDMI output shows
the MCU's boot console (USB devices found, images mounted, then
A2: RESET RELEASED). - The Apple II boots. The slot scan finds the hard disk in slot 6
first — if
hdd1.hdvis mounted, it boots that. Otherwise the HDD ROM falls through to the Disk II in slot 5 and the floppy boots.- Hold open-apple (paddle button 0) during boot to skip the hard disk and boot the floppy instead.
- No images at all: reset releases after ~7 s and the machine boots to BASIC as usual.
- If the MCU firmware isn't running at all, the FPGA releases the Apple II after ~3 s and the card works as a plain video/sound card (no disks, no menu).
Press SELECT on the gamepad at any time:
| Button | Action |
|---|---|
| SELECT | Toggle between the Apple II display and the MCU display |
| Y | In the MCU display: switch between the MENU and the CONSOLE log |
| D-pad up/down | Move selection (hold to repeat) |
| D-pad left/right | Change the highlighted value |
| LB / RB | Change numeric values by ±16 (IP address octets) |
| A | Activate: enter submenu / run action / cycle value |
| B | Back; at the main menu, back to the Apple II |
(Buttons are labeled per SNES-style pads like the 8BitDo SN30.)
Menu screens:
- SLOT ASSIGNMENTS — view the live slot map and reassign cards per slot. Saved changes apply at every boot (before the Apple II starts); "APPLY NOW" reconfigures immediately (then reboot the Apple II). A card can occupy only one slot — assigning it elsewhere empties the old slot automatically. "RESTORE HW DEFAULTS" returns to the table above.
- DISK IMAGES — per-drive mount status; select any image on the storage volume for any drive, or eject a drive. Changes remount immediately and persist.
- STORAGE — storage source: AUTO (USB if present, else SD), USB only, SD only; rescan/remount.
- NETWORK — DHCP on/off; with DHCP off, edit a static IP, netmask and gateway with the pad. Live link status, IP, and MAC.
- USB DEVICES — the USB device tree (hubs, VID:PID, driver, speed).
- FIRMWARE UPDATE — install a new MCU firmware
.binfrom the stick (staged and verified in the background, then installed; the board restarts itself). Shows the installed MCU build stamp. - FPGA UPDATE — install a new FPGA core
.binfrom the stick (the screen goes dark for the 1-2 minute write, then the board restarts itself into the new core). Shows the running core's build stamp. - RESTART MCU — warm-restart the MCU firmware (remounts storage, re-enumerates USB, resets the Apple II).
- RESET SETTINGS TO DEFAULTS — clears all saved preferences.
The bottom of the main menu shows the MCU and CORE build stamps — after any update, check them to confirm what is actually running.
All settings persist in the BL616's flash (not on the stick). The main
menu's diagnostic line (FLASH 4M @3FF000 LD:OK SV:OK) shows the
settings-store state: LD: is the load result at boot (OK, or MAG on a
first boot with no saved settings), SV: the last save (- = none yet
this session).
With the USB-Ethernet adapter connected, the board runs a telnet server on port 23 (the IP is shown on the boot console and in the NETWORK menu). From any machine on the LAN:
telnet <board-ip>
You get the boot/status console (the same log the on-screen console
shows), streamed live. Press m to mirror the on-screen menu and drive
it from the keyboard — up/down arrows move, right-arrow/Enter select,
left-arrow/Esc/b go back, y toggles menu/console, s toggles the
board's display between the Apple II and the MCU, [ / ] are the ±16
keys, c returns to the console stream, q disconnects. Entering the
remote menu switches the board's HDMI output to the menu too (there is
one shared view); press s or back out of the root menu to hand the
display back to the Apple II.
This works with no gamepad attached, and it is the best way to include diagnostics in a problem report: copy the console text instead of photographing the screen.
With the USB-Ethernet adapter connected, the board serves the storage
volume over FTP (port 21) — no more shuttling the stick between machines.
Point Cyberduck, FileZilla or lftp at ftp://<board-ip> (the IP is on
the boot console and the NETWORK menu screen): any username/password,
passive mode (every client's default). Upload, download, rename, delete
and manage folders with your normal file manager; disk serving to the
Apple II keeps running throughout, and files that are currently mounted
as disk images are protected from overwrite/delete (eject them in the
menu first). Plaintext FTP: fine on your LAN, don't port-forward it.
The virtual Super Serial Card (slot 2 by default) is wired to a Hayes-compatible modem emulation with TCP behind it. Any period comm program (ProTERM, Z-Link, ...) configured for a Hayes modem on slot 2 works — any baud rate, since the bridge follows the 6551's programmed speed. Leave hardware flow control off.
AT OK
ATI bridge info + the board's IP
ATDT bbs.example.com connect (port defaults to 23)
ATDT 192.168.1.50:6502 explicit port
+++ then ATH escape (1 s guard) and hang up
When the destination port is 23 the bridge speaks just enough telnet to
keep servers happy. Party trick: ATDT <the board's own IP> connects the
Apple II to the board's remote console (previous section) — a IIgs as a
terminal on its own coprocessor.
The A2N20v2 card's 4-position DIP switch:
- Scanline effect on/off
- Apple II speaker audio over HDMI on/off
- Power-on-Reset hold (delay Apple II start-up until the FPGA is running)
- Apple IIgs mode — set ON when installed in a IIgs
For ROM 00/01 IIgs models the card must be in slot 3 (M2B0 is only present there); ROM 03 models work in any slot.
- Board won't enter boot mode (no
usbmodemport): the UPDATE button must already be held when power arrives. Unplug fully, hold, plug, release. DISK II: WAITING FOR USB STORAGE (PREF)on the console: storage preference is USB-only and no stick is present — insert one or change STORAGE to AUTO.- A drive shows
(EJECTED): you ejected it from the menu; pick an image or "(AUTO)" in DISK IMAGES to restore it. - A drive shows
(NO IMAGE FOUND): none of the default names exist on the volume — add one or pick a file via the menu. - ProDOS/DOS disk boots in an emulator but not on the card: make sure
you're on the current firmware (early betas had a sector-interleave bug
affecting externally-created
.dskimages). Also note.dskfiles written by pre-fix firmware (e.g. COPYA output) need to be recreated. - Settings don't survive power cycles: check the
SV:code on the main menu after changing something — anything other thanOKis a flash write problem; report the code. - Uthernet II apps see no network: check NETWORK for link/IP (the USB-Ethernet adapter bridges the Apple II; the Apple II gets its own IP via the W5100 emulation — see docs/UTHERNET2.md).
- FPGA bitstream: open
a2n20v2_enhanced.gprjin the Gowin IDE (educational or commercial), or build headless with the Gowin CLI — see the project wiki. (When using the Gowin IDE, do not add/remove files from the project or it will rewrite relative paths as absolute.) - BL616 firmware: T-Head RISC-V toolchain + Bouffalo SDK — full
instructions in the BL616 firmware README.
The end-user build is
firmware_host(USB host + disks + menu); thefirmwarebuild is the developer JTAG/UART bridge.
- Project documentation wiki — Gowin CLI setup, architecture, conventions, gotchas
- BL616 firmware, protocol & flashing internals
- Uthernet II emulation · protocol
- Disk image serving design
- Board tasks & status
- Agent & contributor guide