Tutorial 2 - LiteX on Real Hardware
Goal
By the end of this tutorial, you should be able to: build the same SoC from Tutorial 1 targeting your real Tang Nano 9K, synthesize and load it with the same OSS CAD Suite tools from Lab 2, and run both the pre-built BIOS and your own C program on physical hardware, talking to the board over its USB-serial connection.
Prerequisites
- Tutorial 1 completed (LiteX installed, comfortable with the simulation flow).
- Tang Nano 9K connected via USB-C, detected by
openFPGALoader --detect(per Lab 2). - Your Lab 2 constraint file conventions are not reused here - LiteX's board target (below) already knows the Tang Nano 9K's pinout.
1. Build for the real board
litex-boards (installed alongside LiteX in Tutorial 1) includes a target definition for the Sipeed Tang Nano 9K:
Two flags here are not optional, unlike what you might guess from LiteX's own --help output:
--toolchain=apiculais required. The board target's default toolchain (gowin) invokes the proprietary Gowin IDE's own TCL shell (gw_sh) to run synthesis - not part of this course's toolchain, and not installed.apiculais LiteX's name for the open-source path: it resolves tonextpnr-himbaechel, the same tool Lab 2 already has you using. Confirm this in the build log (nextpnr-himbaechelshould appear, notgw_sh).-
--integrated-main-ram-size=0x2000avoids an unpinned download. Leaving this unset makes the board target fall back to configuring the Tang Nano 9K's external PSRAM (HyperRAM) - which, as of this writing, triggers an unauthenticatedwgetof a Python file from a GitHub issue attachment as part of the build, and executes it.0x2000(8 KB) putsmain_ramon-chip (BRAM) instead, sidestepping that path entirely - increase it if your program needs more RAM than that, but always set it explicitly rather than leaving PSRAM as an implicit default. -
--buildruns the full flow: Migen elaboration → Verilog generation → Yosys synthesis → nextpnr-himbaechel place & route → gowin_pack bitstream - the same tools from Lab 2, just invoked by LiteX instead of by hand. --loaduploads the resulting bitstream viaopenFPGALoader, same as Lab 2's manual step.
2. Connect and boot the BIOS on real hardware
Same idea as Tutorial 1's simulated UART, now over the real USB-serial connection:
Power-cycle or reset the board; you should see the same LiteX BIOS banner and litex> prompt as in simulation, now printed from real hardware. If the board is already running (you attached after it booted, rather than catching the very first boot), it won't spontaneously reprint its banner - type reboot at the point you'd expect a litex> prompt.
If --load fails with an openFPGALoader/cable error, check for a stray litex_term process still holding the USB device open from an earlier attempt (pkill -f litex_term, then retry) before assuming a hardware problem - openFPGALoader and litex_term both need exclusive access to the same USB-serial adapter.
3. Load and run your own program
Reuse the C program from Tutorial 1 - the point of building it in simulation first is that it should need no code changes to run on real hardware, only a different load step:
Confirm your program's printf output appears over the real serial connection. If it doesn't, compare carefully against Tutorial 1's simulation behavior - a mismatch between simulation and hardware here is exactly the kind of discrepancy worth debugging methodically (same principle as Project 1's testbench-vs-hardware validation).
4. Optional: observe something physical
For a more satisfying confirmation than serial text, extend your program to toggle one of the board's LEDs via its CSR-mapped GPIO register (LiteX's generated csr.h will include this if your SoC build included a GPIO peripheral) - similar in spirit to Lab 2's blink exercise, but through LiteX's abstraction instead of a hand-written register.
Checklist
- SoC built and loaded onto the Tang Nano 9K via
--toolchain=apicula --integrated-main-ram-size=0x2000 --build --load. - Confirmed the build log shows
nextpnr-himbaechel(OSS CAD Suite), notgw_sh(proprietary Gowin IDE). - BIOS banner and prompt visible over the real serial connection.
- Your own program from Tutorial 1 runs unmodified on real hardware, with observable output.
Next
Tutorial 3 adds a peripheral you write yourself, in SystemVerilog, onto this same SoC's Wishbone bus.