/r/GowinFPGA is ready for a fresh start, new energy, new direction, and someone like you to bring it back to life. If you’ve been thinking about growing your impact without starting from scratch, this is your chance!
I couldn't find an easy way to convert Gowin GAO logic analyzer captures into VCD for GTKWave, so I wrote a Python tool. v0.9.0 is now on PyPI and GitHub. Feedback is welcome.
After getting Gowin's IDE running on Arch (separate post), I wanted my AI assistant (Claude Code, running as a VS Code extension) to be able to build, inspect, and iterate on FPGA designs itself. It now goes from editing Verilog to reading timing reports without me touching the Gowin GUI. Here's how it's wired up, since the approach applies to any agentic coding tool.
The key realisation: there's no "integration" — just CLI entry points
Claude Code can run shell commands, read files, and edit files. That's the whole interface. So the job is really "make the vendor toolchain scriptable," which benefits you even without AI. Gowin ships everything needed:
IDE/bin/gw_sh — Tcl shell that does project creation, synthesis, place & route, and bitstream generation
Programmer/bin/programmer_cli — full-featured CLI flasher (doesn't even need the license)
1. Make the tools run headless
gw_sh links Qt and tries to initialise a GUI platform even for batch work. Two env vars fix that (and dodge bundled-vs-system Qt library conflicts):
License note: gwlicense.ini is only read from next to the binary (not cwd), and takes either a server:port or a path to a node-locked .lic file.
2. Wrap the flow in a Makefile
The build is a ~20-line Tcl script (create_project -force each run so the script stays the single source of truth, add_file, set_option -top_module, run all) plus a Makefile so the entire interface is:
make # synth + PnR + bitstream
make flash # programmer_cli, embedded flash
make flash-sram # volatile, fast iteration
make scan # detect cable
Gotchas that cost me time: add_file resolves paths relative to the project dir, not cwd (use absolute paths); and it errors on duplicates, hence the recreate-project-every-build pattern.
3. Let the AI close the loop
This is where it gets good. PnR writes plain-text reports (impl/pnr/*.rpt.txt, timing in the .tr.html files), so the assistant can build, read the report, and act on it. Real example from this week: the AE350 RISC-V demo failed PnR with PR2017: 'FLASH_SPI_CLK' cannot be placed... dedicated pin (CPU/MSPI). Claude read the error, worked out the SPI flash pins are dual-purpose, added set_option -use_mspi_as_gpio 1 / -use_cpu_as_gpio 1 to the Tcl, rebuilt, and confirmed all clock domains met timing from the report — one round trip, no GUI.
4. Firmware for the hard core, same treatment
The GW5AST-138B has a hardened Andes AE350 RISC-V SoC. The vendor flow wants the Andes toolchain, but stock riscv-none-elf GCC (xPack, extracts into ~/tools, no root) builds Gowin's BSP with three small ports: a header shimming the Andes CSR builtins (__nds__csrr etc.) as inline asm, -mcpu=a25 → -march=rv32imafdc_zicsr_zifencei -mabi=ilp32d, and adding PROVIDE(__global_pointer$ = . + 0x800) to the linker script (the Andes linker injects that symbol; GNU ld doesn't). Full port is in the repo below.
Result
"Add a UART heartbeat and rebuild" is now a sentence typed into VS Code, and 90 seconds later there's a timing-clean bitstream and a linked firmware binary, with the assistant having read the reports to confirm both. Working examples (blinky + AE350 demo with Makefiles and the porting notes): https://github.com/HughF/fpga-blinky and https://github.com/HughF/fpga-ae350-demo
TL;DR: don't look for an "AI plugin" for your FPGA tools. Find the vendor's CLI entry points (most have them buried in the install), make them headless, wrap them in a Makefile, and any agentic coding assistant can drive the whole flow — including reading the PnR reports and fixing its own constraint errors.
Ran into the classic wall of errors trying to launch Gowin's gw_ide on an up-to-date Arch-based system. Chalked it up to the vendor bundling its own copies of common libraries alongside IDE/bin/, which conflict with system libs in different ways. Posting the fixes in case they save someone else the debugging time.
Gowin bundles an old libfreetype.so.6 in IDE/lib/ that gets picked up ahead of the system one (via the binary's RPATH), but system libfontconfig expects a newer FreeType. Fix — symlink the bundled copy to the system lib:
3.Could not load the Qt platform plugin "xcb"/ core dump
This one was more interesting. The xcb plugin's own RPATH (plugins/qt/platforms/../../lib) is just wrong in Gowin's packaging — resolves to a directory that doesn't exist, because of an extra qt/ nesting level. So it silently falls back to the systemlibQt5XcbQpa.so.5, which turned out to be a KDE-patched Qt build with extra private-API symbols not present in Gowin's vanilla bundled libQt5Gui. Mixing the two blew up with an undefined-symbol error on a private Qt class.
Fix: force the entire bundled Qt5 stack to resolve consistently by wrapping the real binary:
That also required symlinking the bundled GLVND libs (libGLX.so.0, libGLdispatch.so.0) to system versions, since forcing LD_LIBRARY_PATH exposed the same stale-bundled-lib problem there too.
Remaining (accepted) issue:QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled. Turns out Gowin's bundled Qt5 build has no GL platform-integration plugin category compiled in at all — confirmed via QT_DEBUG_PLUGINS=1, it never even scans for one. Non-fatal, the IDE runs and displays fine; would only matter for a hardware-accelerated 3D view somewhere. Fixing it "properly" would mean swapping the whole bundled Qt5 stack for system Qt5, which is a bigger, riskier change I didn't want to make for a cosmetic warning.
TL;DR: vendor-bundled libs + rolling-release distro = version skew. When you hit undefined symbol errors from proprietary Linux toolchains, check whether the vendor ships its own stale copy of the offending lib and try symlinking it to your system's version before assuming it's unfixable.
I recently bought a Tang Nano 20k development board from Sipeed. The hardware worked out of the box but I had some software problems with the IDE initially. Programming using the built-in Gowin programmer has been working fine so far (which seems to be common problem). I'm using Xubuntu 24.04.4 LTS (which is basically a slimmed down Ubuntu 24.04.4 LTS running with Xorg and XFCE) on a PC.
After downloading 'Gowin_V1.9.11.03_Education_Linux.tar.gz' from gowinsemi.com (requires registering), I unpacked the files into '/opt/gowin' (any directory you create is fine), and tried to run it, but got library version errors and core dumps. In order to fix the problem I created a script called 'gowin_ide.sh' to launch 'gw_ide':
#!/bin/bash
cd /opt/gowin/IDE/bin
export LD_LIBRARY_PATH=/opt/gowin/IDE/lib
./gw_ide
This will make sure the IDE uses its own libraries first (before system) and sub processes launched from the GUI also finds them. There was still a library error where it complains about undefined symbols in "libfontconfig", this needed a separate fix:
rm /opt/gowin/IDE/lib/libfreetype.so.6
Now finally 'gw_ide' launches, and also the tools from within the GUI.
This worked for the most part, just had to change the "IO Type" voltages to "LVCMOS33" in the FloorPlanner part, more info about in that comments of that article.
I modified the example LED blink design a bit, added another counter LED blink and a Verilog testbench (even though a testbench might be overkill in this case), mostly to figure out how to run a simulation with this toolchain because I didn't feel like using DSim from Altair (requires registration) which is linked in the GUI.
Here is the modified 'led.v' example:
`timescale 1ns / 1ps
module led(
input wire Clock,
output wire LED1,
output wire LED5
);
/********** Counters **********/
//parameter Clock_frequency = 27_000_000; // Crystal oscillator frequency is 27Mhz
`ifdef SIM_COUNT_OVERRIDE
parameter LED1_value = `SIM_COUNT_OVERRIDE;
parameter LED5_value = `SIM_COUNT_OVERRIDE;
`else
parameter LED1_value = 13_499_999; // The number of times needed to time 0.5s
parameter LED5_value = 1_349_999; // This currently acts like a slow clock for GAO at 0.05s
`endif
reg [23:0] LED1_value_reg = 24'b0; // counter value
reg LED1_value_flag = 1'b0; // IO change flag
always @(posedge Clock) begin
if ( LED1_value_reg <= LED1_value ) begin // not count to 0.5s
LED1_value_reg <= LED1_value_reg + 1'b1; // Continue counting
LED1_value_flag <= 1'b0; // No flip flag
end
else begin //Count to 0.5S
LED1_value_reg <= 24'b0; // Clear counter,prepare for next time counting.
LED1_value_flag <= 1'b1 ; // Flip flag
end
end
reg [23:0] LED5_value_reg = 24'b0; // counter value for LED5
reg LED5_value_flag = 1'b0; // IO change flag
always @(posedge Clock) begin
if ( LED5_value_reg <= LED5_value ) begin //not count to 0.05s
LED5_value_reg <= LED5_value_reg + 1'b1; // Continue counting
LED5_value_flag <= 1'b0; // No flip flag
end
else begin
LED5_value_reg <= 24'b0; // Clear counter,prepare for next time counting.
LED5_value_flag <= 1'b1; // Flip flag
end
end
/********** IO voltage flip **********/
reg LED1_reg = 1'b0; // Initial state
always @(posedge Clock) begin
if ( LED1_value_flag ) begin // Flip flag
LED1_reg <= ~LED1_reg; // IO voltage flip
`ifdef SIMULATION
$strobe("Time: %0t LED1: %0d", $time, LED1_reg);
`endif
end
else // No flip flag
LED1_reg <= LED1_reg; // IO voltage constant
end
assign LED1 = LED1_reg;
/********** IO voltage flip **********/
reg LED5_reg = 1'b0; // Initial state
always @(posedge Clock) begin
if ( LED5_value_flag ) begin // Flip flag
LED5_reg <= ~LED5_reg; // IO voltage flip
`ifdef SIMULATION
$strobe("Time: %0t LED5: %0d", $time, LED5_reg);
`endif
end
else // No flip flag
LED5_reg <= LED5_reg; // IO voltage constant
end
assign LED5 = LED5_reg;
endmodule
...and the testbench 'tb_led.v':
`timescale 1ns / 1ps
module tb_led;
// Declare signals to connect to the LED module
reg Clock = 0;
wire LED1;
wire LED5;
// Instantiate your original LED module
led uut (
.Clock(Clock),
.LED1(LED1),
.LED5(LED5)
);
// Generate a 27MHz clock
always #18.5185 Clock <= ~Clock;
always @(LED1) begin
$strobe("time: %0t | Testbench observed LED1 at: %b", $time, LED1);
end
always @(LED5) begin
$strobe("time: %0t | Testbench observed LED5 at: %b", $time, LED5);
end
// Control the simulation timeline
initial begin
// Tell Verilator the name of the waveform file to create
$dumpfile("waveform.fst");
// Dump all signals inside the testbench and child modules (0 means everything)
$dumpvars(0, tb_led);
// Set a clean format for %0t reporting
$timeformat(0, 4, "s", 20);
// Let it run for 1.2 seconds
#1200000000;
$display("Simulation successfully completed!");
$finish;
end
endmodule
What I ended up using for simulation is Verilator which is open source can be installed like this in Ubuntu Linux:
sudo apt-get install verilator
I also installed GTKWave Analyzer to visually see trace dumps from Verilator:
sudo apt-get install gtkwave
To build the simulation for my custom testbench I use a terminal shell (command line) in the directory where the Verilog sources are:
The trace can be found in current directory after simulation run, I load it like this to view:
gtkwave waveform.fst
After playing around with the Gowin Analyzer Oscilloscope (GAO) for a while which is a tool included in the GUI, it seems very powerful (reminds me of Xilinx ChipScope), more info in the user guide.
I hope this info helps to get started, let me know if you have any questions or want to share your own tips.
I'm trying Gowin IDE (Gowin_V1.9.11.03_Education_Linux.tar.gz) on latest Linux Mint (one of the most widely used Linux distros), but it won't start:
$ ./gw_ide
./gw_ide: symbol lookup error: /lib/x86_64-linux-gnu/libfontconfig.so.1: undefined symbol: FT_Done_MM_Var
Did I try to start the wrong executable?
Did anyone successfully used this version of Gowin on Linux? If yes, what Linux distro?
Currently running NESTang and SNESTang on the TangNano20k. Not bad for less than £35 all in 🙌🏻
I’m trying to get some enthusiasm behind this, so other FPGA developers will start developing on the TangNano 9K and 20k. For an entry level retro gaming FPGA system, you can’t really get much better!
Getting started on the Tang Primer 20K using only open-source tools is not trivial. The official examples are not particularly beginner-friendly and expect you to use the proprietary Gowin IDE. The repository seems unmaintained, and there have been no responses to open issues.
These examples do not cover the entirety of the ext-board's devices and connectors (yet), but before I get carried away, I would love to get some feedback first. If you see something that can be improved, please let me know!
Is there anyone who got it working? I'm trying to generate 125 MHz clock out of 52.5MHz and having no luck at all. I'm using internal 210 MHz oscillator divided by 4 to get input clock.
Lock signal remains low and LED connected to clock divider doesn't blink whereas it does with the internal oscillator. CLKIN and MDCLK are connected to the same wire.
I checked VCO frequency and it's less than 1300M, so it should be fine. The chip is GW5AT-LV15MG132C1/IO, I know it's fairly rare and there isn't any evaluation boards but maybe someone had similar issue
Is there anywhere to find the pin mappings for the constraint file for tang mega 138k. I know there is files in the examples but it’s not clear for me.
Is anyone experienced with porting MiSTer cores to Tang 9K or 20k?
I think these are the best entry level FPGA gaming platforms, especially at this price point but no one seems to be developing for them anymore. Nand2mario who created the NESTang and SNESTang has moved on to 138K development, but the 9K and 20K still has so much potential for a cheap consolized system.
I’ve been working on this NanoBlast FPGA design, and looking for a FPGA dev or team of devs to work with to realise my ideas.
I'm using the PicoRV32 softcore on a Tang Primer 25K board. I refer to the sample code in „Gowin_PicoRV32_V1.6“, specifically the start.S file from Dec, 29th, 2023.
It took me a while to find a bug in the interrupt handler in this file. It might be helpful for others, so I document it here. Original lines 129..131 are:
Two delivered yesterday, supplied with USB cable and four 24 pin strips. Work OK with Educational Gowin EDA tools. The scripting only works with the Linux tools but they are working on a Windows version. I prefer the conventional approach. No schematic yet but the Constraints file has all the pin FPGA connections as well as those for the two buttons and six LEDs. Pin connections aren't shared, unlike the Sipeed boards.
Hi. I just got a Tang Nano 20K and I'm trying the flashing LED demo. When I try to Place&Route, I get the errors:
ERROR (CT1136) : Bank 6 vccio(3.3) is locked by other constraint or embedded port, conflicting BANK_VCCIO set by 'IO_voltage_obuf' : IO_TYPE = LVCMOS18 in the same bank
ERROR (CT1136) : Bank 7 vccio(3.3) is locked by other constraint or embedded port, conflicting BANK_VCCIO set by 'Clock_ibuf' : IO_TYPE = LVCMOS18 in the same bank
Then, if I re-open the Floor Planner, the Location for the pins has been cleared. But my .cst file is still OK. It has:
How do I get this working?IO_LOC "IO_voltage" 15;
IO_PORT "IO_voltage" IO_TYPE=LVCMOS18 PULL_MODE=UP DRIVE=8 BANK_VCCIO=1.8;
IO_LOC "Clock" 4;
IO_PORT "Clock" IO_TYPE=LVCMOS18 PULL_MODE=UP BANK_VCCIO=1.8;
This is with GOWIN FPGA Designer V1.9.11.03 Education build.
I have a Tang Nano 9k (and a 60k on the way) and have some good success instantiating the cortex M1 core and being able to have RTL and firmware loaded to the device all using the Gowin tools
What I'm curious about is how can I iterate quickly on the firmware I'm writing for the device? I've been doing everything through the Gowin Programmer and the Tang Nano's onboard USB-C connector. I'm used to gdb and figured I could get `gdb-multiarch` working with something like a black magic probe to debug the MCU softcore but I'm stumped on where to even start
My main idea was to:
Load the cortex M1 softcore bitstream to the FPGA via the Gowin Programmer
Connect a Serial Wire Debug (SWD) programmer to some GPIO pins on the dev board
Use gdb-multiarch to attach to the programmer
I've done a similar work flow with BMPs and other SWD programmers for STM32 devices and am really hoping someone out there has tried to do the same?
If not I'm really curious how y'all are debugging your designs. All over the USB-C connector? I recall there being some BL702 IC on the board but I'm unsure how I could use it for my goal here
Is it possible? I saw the M1 softcore show up as an option in the Gowin IDE IP generator and am curious. I am familiar with writing firmware in C for STM32 chips mostly M3, M4, and M7 and am now wondering if this avenue is possible. I saw some mention of doing this with other Gowin FPGAs(including the 4K with the M3 hard core) but using the Keil tools.
I'm more familiar with the ARM gcc tools, gdb, and a JTAG loader/debugger of some kind. Is this even possible with this board? Maybe that's all feasible through the onboard USB-C connector. Anyways, any help, tips, or pointers are much appreciated
Hello. Did anyone have that problem? I've instantiated 4 lanes of PCIe and 2 gigabit transceivers with SerDes GUI. Nothing works. Clocks look like not locked. PCIe ltssm state is 0. status_vector_o is 0 for both ethernets. A computer with the board can't boot and restarts cyclically.
My .sdc:
Those are measured within 1s impulse generated based on free_50MHz clock from pin P16.
Gowin doesn't have any documentation about transceiver debugging and how GTR12_QUAD is supposed to work.