F4PGA open source flow gets a new Python-based build system and CLI tool
Published:
Topics: Open FPGA, Open source tools
As a long-time Platinum member of the CHIPS Alliance, Antmicro takes an active part in developing and supporting an open source and collaborative approach to all aspects of hardware design. In this spirit, in a joint effort with Google, AMD Xilinx, University of Toronto and others, a few months back we introduced the FOSS Flow For FPGA (F4PGA) Workgroup, whose aim is to drive open source tooling, IP and research efforts for FPGAs. F4PGA, previously known as SymbiFlow, serves as an umbrella project for several activities, all of which lead to the end goal of a complete FOSS FPGA toolchain.
One of the most recent projects developed within the workgroup is the unified f4pga CLI tool. In the broader context of our continuous efforts to make the FPGA space more unified and flexible, creating the f4pga CLI tool was a logical next step - it allowed us to wrap the underlying tools into a single CLI, making the F4PGA toolchain a more complete flow. The currently supported architectures are AMD’s (former Xilinx) 7 Series, Lattice’s iCE40 and QuickLogic’s EOS S3. To illustrate how it works in practice, this note will explain how to use this utility to synthesize, P&R and eventually build the bitstream for a 7 Series device.
Overview of the F4PGA flow
So far the F4PGA flows generating bitstreams for various FPGA targets were invoked using a variety of bash scripts which didn’t share a consistent interface; this was related to reliance on specific vendor databases and tool versions which made it non-trivial to implement a unified approach. We have been working towards changing that for a long time and the goal has always been to provide a single entrypoint and single configuration interface for all flows. To achieve this, we started developing the eponymous F4PGA Python package (albeit lowercase, so f4pga) that will serve as a complete front-end for the toolchain. This front-end is designed to provide new users with a simple interface that works well for all supported platforms, as well as to give toolchain developers an opportunity to easily integrate their tools or custom flows into F4PGA.
The main F4PGA flow is depicted in the diagram below. Yosys is used for the synthesis of the design, then, depending on the target architecture, VPR or nextpnr is responsible for the P&R process which passes on the results to the device specific bitstream generation tool.
The unified f4pga interface provides the necessary glue to unify the user experience between e.g. VPR and nextpnr, which typically feature different internal plumbing. VPR may require some extra steps in the flow that need to be performed in order to get a desired placement and routing, which increases the total number of stages within a flow for an FPGA. While this is the “main” flow, some devices, such as Lattice FPGAs, use nextpnr for place & route instead of VPR. Such flows can be handled by the f4pga package as well, allowing users to use the same interface to run flows consisting of completely different tools.
f4pga design
The f4pga Python package takes a modular approach to constructing flows. Each tool/script present within a flow comes with a Pythonic wrapper that provides F4PGA with extensive metadata. This allows F4PGA to create a flow on its own as long as all modules and dependencies required for a target are present. “Platform flow definitions” that come bundled with f4pga provide a set of overridable defaults and configurations that allow users to easily get started building their FPGA projects.
This is a layered, “sandwich” design, with the top layer being the configuration provided by the user. That’s where the user provides project-specific paths and values, such as location of Verilog sources or name of the top module.
One layer down is the flow definition, which defines a set of tools pre-configured with a given platform in mind. These pre-configured tools are called “stages”. Flow definitions normally come as JSONs bundled with f4pga, and custom flows can be written by users as well.
The lowest layer (f4pga package aside) are f4pga modules which define the logic of execution and metadata of used tools. Those are Python scripts that are dynamically loaded by f4pga. If you are a developer interested in learning more about the design of f4pga, and possibly extending this system, check our docs.
Below is a graph illustrating how this design works for constructing an example flow:
Each stage in the constructed flow gets instantiated from the pool of available stages described in a flow definition along with their defaults (in the middle layer). This pool makes use of the f4pga modules, which wrap executable tools and are provided along the package (bottom layer). The user configuration (top layer) is used to provide any extra files and values associated with the user’s project. This results in constructing a flow with relationships between layers and stages fully resolved and the execution order calculated.
Since the user provides only a small fraction of the entire configuration, you might be wondering how the paths of some dependencies in the flow are handled. Fortunately, f4pga is capable of generating default paths for all products, given the input files. This saves the trouble of configuring each product and dependency of every step of the flow.
f4pga features
The new tool introduces some new features which weren’t possible before. The new f4pga package handles incremental builds, skipping re-building targets that are already up-to-date. This also applies to targets depending on other targets that remain unchanged after rebuild (e.g. blif after changing a comment in a Verilog file). Another addition is the ability to easily set up a single project targeting multiple devices, thanks to per-part configuration overrides. To make the configuration portable and easy, we use JSON files for storing a set-up for a project.
For power-users, the biggest feature might be extensibility and configurability. You can easily change or limit the targets that are generated, include your own configuration for the PnR tool or add a custom script to a flow.
f4pga usage
The recommended way to use f4pga is by using flow configuration files. Those are short JSON files that store configurations for a project. You can find an example of such a file in the f4pga-examples repo.
{
"default_part": "XC7A35TCSG324-1",
"values": {
"top": "top"
},
"dependencies": {
"sources": [
"counter.v"
],
"synth_log": "synth.log",
"pack_log": "pack.log"
},
"XC7A35TCSG324-1": {
"default_target": "bitstream",
"dependencies": {
"build_dir": "build/arty_35",
"xdc": [
"arty.xdc"
]
}
}
}
As mentioned in the “f4pga design” section, those configurations list source files and values to be read by the tools in the flow, but they can also be used to configure output paths and generate extra targets (e.g. Yosys synthesis logs) if the user demands it.
A configuration file at its core features two dictionaries: “values” and “dependencies”. Entries in those dictionaries configure and override defaults provided by the f4pga package and flow definition. Those dictionaries can be nested on several levels: globally (applied for the entire fow), per-part (applied only when the flow is run for a specific part) and per-stage (applied only for a given stage in a flow).
More information on the configuration file and some more specific usage examples can be found the f4pga documentation
For the mentioned “counter” example, simply navigate to the project directory and run:
f4pga build -f flow.json
to generate a bitstream for a default part (XC7A35TCSG324-1).
The f4pga CLI tool currently has two levels of verbosity. When run with one or two -v
flags it will print the status of a project, listing detected dependencies and their detected status (new/up-to-date/scheduled for build/rebuild). You can use the pretend mode (-P
) to check the status without building anything.
If you don’t want to use the json files, the configuration can be passed “inline” when invoking a command. This can also be used for making quick, temporary overrides to configurations without modifying the flow configuration file.
Since f4pga is a Python library it can be easily integrated into existing frameworks that need a versatile tool for building designs for various FPGAs such as LiteX, where we used the library to provide an easier interface towards the open source toolchain for Xilinx’s 7 Series devices.
The new F4PGA build system currently comes with support for the following platforms:
- Xilinx 7 Series
- xc7a35t/xc7a50t
- xc7a100t
- xc7a200t
- QuickLogic PolarPro3
- EOS-S3
- Lattice iCE40
- LP/HX
- Ultra/UltraLite
- UltraPlus
Future developments of the f4pga tool
We are working on improving the overall user experience of f4pga, including the output messages of the tool. We are also considering using it as a means of simplifying the installation and update process of the toolchain itself, i.e. fetching and installing required data through a subcommand, instead of following multiple steps from documentation.
Another potential improvement involves a Chisel module that could generate Verilog out of Scala sources, seamlessly integrating them into the flow, allowing Chisel code to live next to Verilog; or a module that can use Vivado to generate bitstreams. Additionally, once fpga-interchange becomes a suitable replacement for the current flows, creating an fpga-interchange flow definition will become the next natural step.
All the efforts described above, both current and future, will lead to broader accessibility and maturity of the F4PGA toolchain. Antmicro offers advanced engineering services, helping our customers not only adopt this flow, but also create portable FPGA systems that leverage such open source tools.