Beginner / 45 min
Send your first ping
Let's get the existing example running before changing it. That gives us a useful starting point: if something stops working later, we'll know which change to look at first.
The terminal commands below use a macOS or Linux shell. On Windows, use WSL for the FPGA build and your normal Windows tools to copy files onto the board. Run the commands on your computer, not in CircuitPython's serial console.
Prepare CircuitPython
Back up anything already on the board. Download the stable firmware from the RPGA Feather CircuitPython page. Hold BOOT while plugging in USB, release it, and copy the .uf2 onto the RPI-RP2 drive. After the board restarts, you should see CIRCUITPY.
Get the bundles matching your firmware's major version from the CircuitPython libraries page. You'll need the Adafruit bundle and the community bundle. Create a lib folder on CIRCUITPY if it doesn't exist, then copy these library files or folders into it:
oakdevtech_icepython.mpyfrom the community bundle. This loads the FPGA design.adafruit_bus_devicefrom the Adafruit bundle. This is a dependency of IcePython.adafruit_pioasm.mpyfrom the Adafruit bundle. This prepares the PIO instructions.
Some source downloads provide .py files instead of .mpy files; those can serve the same role. Keep one version of each library. rp2pio, the module that operates PIO, is part of the board's firmware rather than a file to download.
Open a CircuitPython serial console so you can read the program's output. Ctrl+C stops a running program; Ctrl+D reloads it.
Build the FPGA design
Download and extract OSS CAD Suite using its installation instructions. It includes the tools that turn Verilog into an FPGA configuration file, called a bitstream.
In a terminal, choose a folder for the project and run:
git clone https://github.com/skerr92/RPGA_python_net.git
cd RPGA_python_net
make build OSS_CAD_SUITE="$HOME/Documents/oss-cad-suite"
Replace $HOME/Documents/oss-cad-suite with the location where you extracted the suite. Choose a location without spaces, since this project's Makefile uses the path to run its tools. The finished file is top.bin in the project's main folder.
The build uses Yosys to turn the design into FPGA logic, nextpnr to fit and connect that logic, and icepack to package the result. You don't need to learn all three tools before trying the project. For now, a completed build and a new top.bin are the useful checkpoints.
Copy the project onto the board
Stop any existing program with Ctrl+C. Copy top.bin and rpga_py_net.py from the project folder onto CIRCUITPY, then copy its code.py last. Your drive should contain:
CIRCUITPY/
code.py
top.bin
rpga_py_net.py
lib/
oakdevtech_icepython.mpy
adafruit_pioasm.mpy
adafruit_bus_device/
Use the repository's code.py as the starting program. The helper file also has a generic example using GP pin names, but the RPGA program selects the board's F2, F3, F4, and F6 connections and the extra signals this design needs.
You don't need external jumper wires for the intended onboard connections. Leave external circuits disconnected while following this example, including anything on D8. The design uses several shared connections for status signals after loading the FPGA.
Press Ctrl+D in the serial console. The program loads top.bin, releases the SPI programming pins, and reuses those pins for the network link's status signals. That's why the Python files and FPGA bitstream need to come from the same version of the project.
Read the result
The current example runs a PIO test, then repeats the exchange using Python-controlled pin changes, called bit-banging. That second run gives us a comparison when we're working on timing.
Look for these checkpoints among the diagnostic lines:
programmed FPGA from top.bin in ... seconds
PIO ready
link driver PioOneBitLink
ARP reply src 192.168.1.10 02:52:50:47:41:01
ICMP reply ok ident 0x1234 seq 1 payload b'pio-smoke'
The time will vary, and other diagnostic lines appear between these messages. The final summary reports ok and bad for each run. An ok True means the exchange reached a parsed echo reply; check the FCS status and bad result too. FCS is the frame check sequence, a value used to detect damaged Ethernet frames. A parsed reply with a failed frame check still needs attention.
This is an experimental learning project with timing and receive diagnostics still exposed. A failed run gives us something to investigate; don't spend your first afternoon changing every timing setting at once.
Make one small change
Find this argument in code.py:
payload=b"pio-smoke",
Change it to another short message, such as payload=b"hello-fpga",, and save. Keep the leading b: it tells Python this is a sequence of bytes. Reload and look for the new message in the echo reply.
Now you've changed something that travelled into the FPGA and came back. That's a much more useful starting point than a screen full of unexplained numbers.
If the exchange stops early
- No
CIRCUITPYdrive: check the data cable and repeat the board-specific firmware installation. - A missing-library error: read the module name in the console and check
lib. Match compiled.mpylibraries to your CircuitPython major version. - PIO isn't ready: check
adafruit_pioasmand the RPGA firmware.rp2piocomes from firmware; copying another library won't add it. - The FPGA loads but ARP fails: copy a freshly built
top.bintogether with both Python files. Confirm the files came from the same checkout and remove external connections from shared pins. - ARP works but the echo fails: keep the address settings unchanged and compare the PIO and bit-bang results. Save the full console output, including
RX statusand FCS details, before trying a timing change.
If Make can't find its tools, check the OSS_CAD_SUITE path. If your board firmware lacks an F_ pin name used by the program, confirm that you installed RPGA Feather firmware rather than generic Pico firmware.
Keep exploring
The next useful files are rpga_py_net.py, which builds and reads the messages, and rtl/UDP_switch.v, which handles the FPGA's replies. Despite that filename, our first project uses ARP and ICMP, not a general-purpose UDP application.
Start by following one message through the networking repository. Find where Python builds it, then where the FPGA decides how to answer. You don't have to understand every line to begin connecting the pieces.
If you'd like to give the FPGA a processor of its own, try getting started with OASIS and DungV next. It's another way to use the same pair of chips, with the RP2040 helping us load and explore a small CPU inside the FPGA.