Building a Stand-alone JTAG FPGA Flasher
Field Programmable Gate Arrays (FPGAs), are powerful programmable logic devices that are commonly used in high-performance / high-complexity low-volume electronic designs. FPGAs differ from many other programmable electronic devices, such as micro-controllers, as the software deployed to them doesn’t “execute”. This software actually configures the structure of the FPGA hardware, such that it can perform advanced combinational functions efficiently in real-time.
The software can be loaded into to the FPGA either by a supervisory system loading it into SRAM on boot, or it can be configured to load itself via a stored binary in non-volatile memory. In both cases, there needs to be an external process to provide this binary to the FPGA. In some designs, where the FPGA is the primary control system, this binary needs to be provided (or “flashed”) to the device during product manufacture. The recommended process for “flashing” FPGAs varies vendor to vendor, device to device. You either get stuck with proprietary systems, where you are entirely reliant on a third-party for device support or you have to program the flash manually, which comes with it’s own considerations (eg: multi-master SPI). The problem with either of these is, as a Test Engineer, it creates a process with key factors that are outside of your control. The FPGA developer doesn’t support or breaks sharing the SPI flash? Now you have unreliable programming / upgrading of devices. Xilinx doesn’t support certain flash devices? Now you have limitations on what components can be used on the device. Call me a control freak, but I like to keep as much as possible under our team’s control, such that if something breaks I don’t have to rely on someone else to fix it.
This is where OpenFPGAFlasher comes in, which is an opensource tool that mimics some of the functionality of these proprietary tool-chains in a vendor/device agnostic manner. Of note, it can create a JTAG-to-SPI bridge, which essentially is a tiny fpga-binary that fires up the SPI lines on the FPGA, this allows programming the SPI flash via a JTAG link. This is more complex that it may seem, as not only every device, but every single package that that device exists as, requires a unique SPI-to-JTAG binary. Sadly, none of the packages I required had existing functionality in the tool. Fortunately though, this project provides parametrized generic vhdl and verilog source and a electronic design automation (EDA) pipeline to automatically generate these. With such an active core dev, I just had to raise a few pull requests against the repository and these devices are now added to the main project.
So we have the tool, but how are we going to run this? With recent focus on cyber security, and the clamp down IT systems, it’s made some parts of Test Engineering quite tricky, particularly as it relates to anything requiring permission elevation (installing drivers, accessing certain device settings ect.). I have been finding a lot of success in moving as much of the complexity as possible off of the Windows test PCs, and onto single board computers (SBC) that are controlled via API on an dedicated isolated Ethernet NIC. That way it essentially becomes a test instrument with the added benefit that there is there’s significantly less to configure on the test PC. For this project’s SBC, I chose the RaspberryPi 4, it features USB 3, gigabit Ethernet and a huge community and user-base (There’s good reason why commercial application causes huge supply problems). So we just need to build an ARM-64 binary for OpenFPGALoader from source on a headless 64-bit install of RPI-OS and we are most of the way there already.
So then we need to create some sort of API to allow us to remotely control this thing. As a Python fiend I chose FastAPI, which is a super quick way to develop APIs using pydantic models. There’s a couple of different actions we need to support:
api_upload_binary– Move a FPGA binary file from the test PC to the RPI device.
api_binaries– Return a JSON list of binaries already stored on the RPI device.
api_identify– Scan the JTAG chain using OpenFPGAFlasher to ensure communication with your device.
api_program– Program a binary to SPI flash using OpenFPGALoader via JTAG to your FPGA device.
api_erase– Bulk erase the contents of SPI flash using OpenFPGALoader.
As well as a few convenience endpoints for controlling the device:
api_version– Return the version of the deployed API / Flasher package.
api_reboot– Do a full reboot of the raspberry PI.
logs– Render the last 100x lines of python / systemctl logs for the flasher service.
The API is extremely simple to implement, the complexity lies more in managing the sub-processes of
OpenFPGALoader and ensuring all cases are handled. The following shows an example execution of verifying the JTAG chain devices and Flashing a device (devices and images names have been changed for confidentiality).
>> curl "http://fpga-flasher.local:8000/api/identify?" "[\"xc7k420\"]" >> curl "http://fpga-flasher.local:8000/api/program?fpga_part=xc7k420tffg901&binary=Example_v1.0.0.mcs" "\"Success! Flashed Known Device microchip SST26VF032B 64\""
Pretty much much anything that can fire off HTTP requests can control this instrument, which makes integration on an automated tester a breeze. With the user interactable swagger API, it’s also a useful tool for product development teams that can manually use it via the web front-end. All in all I was super stoked with this solution, it provides such an elegant workaround to a bunch of really clunky tool-chains I was using in the past.
Huge shout out to Gwenhael Goavec-Merou (trabucayre), who is just an absolute legend for writing and actively maintaining this OpenFPGALoader (as well as being just a nice person). As an FYI both this blog post and my contributions to OpenFPGALoader were disclosed to my employer prior to either occurring, as they relate to work done as part of my role.