AutoFPGA - An FPGA Design Automation routine
After now having built several FPGA designs, such as the xulalx25soc, s6soc, openarty, zbasic, icozip, and even a Basys-3 design of my own that hasn't been published, I started recognizing that all of these designs have a lot in common. In particular, they all have a set of bus masters, such as the UART-to-wishbone bridge that I use, the hexbus debugging bus that offers a simpler version of the same, or even the zipcpu. Many of these designs have also started to use (and reuse) many of the peripherals I've developed, such as the generic UART, the QSPI flash controller, the SD-card controller, the block RAM controller, the RMII Ethernet Controller, the Real-Time Clock, the Real-Time Date, the Organic LED controller, Xilinx's Internal Configuration Access Port, the wishbone scope, the GPS controlled clock, or even the PWM Audio Controller. All of these peripherals have a very similar format when included within a top level design, all of these require a certain amount of care and feeding as part of that top level design, but yet rebuilding that top level design over and over just to repeat this information becomes a pain.
Where things were really starting to get annoying is where the C++ information was depending upon Verilog information. A classic example of this is the base address of any bus components. However, if you add clock rate into the mix, you can then also control things such as any default UART configuration, default clock stepping information (for the RTC clock), or even default video clock information.
Sharing information between Verilog and C++ then became one of the primary reasons for creating autofpga. While peripheral decoding is typically done in some main Verilog file, other files depend upon what this peripheral decoding is. These other files include the host register definition file (used for debugging access), the register naming file, the ZipCPU board definition file used by newlib, the linker script used by the compiler, and even the LaTeX specification for the board. Creating and updating all of these files by hand anytime I create a new board file can get tedious. Further, every time a board is reconfigured, the constraints file, whether XDC or UCF file, needs to be updated to match the current constraints.
Solving this multi-language coordination problem is the purpose of autofpga.
Unlike many of the other tools out there, such as Xilinx's schematic capture, autofpga is not built with the clueless beginner in mind, neither is it built to hide the details of what is going within the project it creates. Instead, autofpga is built with the sole purpose of alleviating any burden on the FPGA designer who otherwise has to create and maintain coherency between multiple design files.
That this program facilitates composing and building new designs from existing components ... is just a bonus.
The goal of AutoFPGA is to be able to run it with a list of peripheral definition files, given on the command line, and to thus be able to generate (or update?) the various board definition files discussed above:
- build.xdc (Created by modifying an existing XDC file)
- doc/src/(component name).tex (Not started yet)
Specifically, the parser must determine:
If any of the peripherals used in this project need to be configured, and if so, what the configuration parameters are and how they need to be set. For example, the UART baud rate and RTC and GPS clock steps both need to be set based upon the actual clock speed of the master clock. Placing a clock module within the design that sets up a clock and declares its rate is the current method for accomplishing this.
If peripherals have or create interrupts, those need to be found and determined, and (even better) wired up.
If an autofpga configuration file describes one of the following classes of items, then the file is wired up and connected to create the necessary bus wiring as well.
Full bus masters
Peripheral selection and decoding logic is added in
(Partial) bus masters, wanting access to one peripheral only
One-clock Peripherals (interrupt controller, etc.)
Two-clock Peripherals (RTC clock, block RAM, scopes, etc.)
o These need a line within the linker script, and they need to define if their memory region, within that linker script, has read, write, or execute permissions
Peripheral files need to be able to describe more than one peripheral. For example, the GPS peripheral file has a GPS-Clock, GPS-TB to measure the performance of the GPS clock, and a serial port (WBUART) to allow us to read from the GPS and to write to it and so configure it. Of course ... this also includes a parameter that must be set (baud rate) based upon the global clock rate.
Some peripherals might exist at multiple locations within a design. For example, the WBUART serial component can be used to create multiple serial ports within a design.
To handle this case, the WBUART configuration file may be subclassed within other component configuration files by defining a key @INCLUDEFILE=wbuart.txt. This will provide a set of keys that the current file can then override (inherit from).
TODO: Subclass only named components of a configuration file
Some peripherals need to be able to perform math on a given value to determine an appropriate setting value. These peripherals need access to variables. The classic examples are the baud rate, which depends upon the clock rate, as well as the step size necessary for the RTC and the GPS clocks, both of which also depend upon the master clock rate.
This feature is currently fully supported using integer math.
This project has moved from its bare infancy to an initial demo that is now working on a Nexys Video board. You can see the code this program generates in the demo directory, although you may have to collect the actual peripheral code from elsewhere. (Most of it already exists in the openarty project.)
Simple bus components ... just work.
Components with logic in the toplevel work nicely as well.
AutoFPGA can now support multiple, dissimilar clock rates. Users just need to specify a clock to generate it. The clock is then made available for configuration files to reference
o AutoFPGA now also has support for building a test bench driver that would match multiple on-board clocks, and even a main Verilator test bench module that will declare and call any C++ simulators associated with the various clocks and clock rates at the appropriate time in your simulation.
Addresses get assigned in three groups, and processed in three groups: components having only one address, components having more addresses but only a single clock delay, and all other components and memories
Multiple bus support is now included, to include creating arbiters to transition from one bus to the next, as well as keeping track of what the final addresses are for devices on an upper level bus.
This makes it possible for the SDRAM to be on one bus, supporting video reads/writes, and for the CPU to be able to access that bus as well--as a sub-bus of the main peripheral/memory bus.
Interrupts get assigned to a named controller, and then C++ header files are updated to reflect the interrupt assignments
A simple integer mathematical expression evaluator exists, allowing simple math expressions and print formats. This makes it possible to set a global clock frequency value, and to then set baud rates and other clock dividers from it.
The auto builder now creates a master C++ Verilator simulation class file, calls any verilator simulation support routines, and organizes these by whever clock rate they are placed upon.
A Makefile include is created for the Verilog (RTL) directory, so as to facilitate the modifications necessary for integrating the new component into the verilator simulation build
o Discovering the --MMD Verilator option has since rendered the dependency work largely unnecessary
Only one type of address building is supported. I'd like to be able to support others, but so far this has been sufficient for my first project.
o Likewise, the project only supports WB B4/pipelined. No support is provided for WB B3/classic (yet), although creating such support shoud not be difficult at all.
AutoFPGA now builds a ZipCPU Linker Script for the project
The LaTeX specification table building isn't there ... yet.
Specific Test Case
A specific test case that I'm trying to support is one that will build a system for Digilent's Nexys-4 Video, with not only a 100MHz ZipCPU on board accessing a 32-bit wishbone bus, but also a 100MHz DDR3 SDRAM existing on its own 128-bit wide wishbone bus, together with two HDMI components (148.5MHz for 1080p) that will also write to and then read from this 128-bit bus. The DDR3 wishbone bus will then go through a bridge to become an AXI4 bus. The video test case I am hoping to support is 1080p at 60Hz, which should use up over 50% of the memory bandwidth on the device.
Sample component files
Component files now exist for many of the components I've been using regularly. These include: a Flash controller, block RAM, a UART console, a very simple GPIO controller, RMII ethernet controller, MDIO ethernet control interface, a GPS UART and PPS-driven internal clock, a Real-Time (GPS driven) Clock, a PS/2 Mouse, an OLED component, and more. (I'm currently working on the SDRAM ...) Many of these component cores exist and have their own repositories elsewhere. For example, the wishbone UART core may be found here. Building the cores themselves is not a part of this project, but rather figuring out how to compose multiple cores into a top level design from both cores and component descriptions.
AutoFPGA is designed for release under the GPLv3 license.
Should you find the GPLv3 license insufficient for your needs, other licenses can be purchased from Gisselquist Technology, LLC.
Likewise, please contact us should you wish to guide, direct, or otherwise fund the development of this project. You can contact me at my user name, dgisselq, at the wonderful ieee.org host.