How to Import 100 Registers from a Text File to airhdl

One of our users recently asked how he could import a list of 100 registers into airhdl without having to create every single register by hand in the user interface. He had the names of the registers available in a text file, one name per line:



Subscribers to the airhdl Professional CL plan can import register maps into airhdl by uploading a JSON file whose structure is described in the documentation.

To help import the user’s registers into airhdl, we have created a small Python script that transforms the user’s text file into an airhdl register map JSON object. The script, which is called, is available on our GitHub page. It can be used as follows:

python registers.txt map_name

Where registers.txt is the path to the text file containing the register names and map_name is the name of the register map to be created. The script generates an output file called out.json in the current working directory, which looks like this:

  "jsonVersion": 2,
  "registerMap": {
    "name": "my_map",
    "description": "",
    "width": 32,
    "baseAddress": 0,
    "addrWidthBits": 32,
    "revision": 1,
    "generateRecordPorts": false,
    "registers": [
        "type": "Register",
        "name": "REG_0",
        "description": "",
        "access": "READ_WRITE",
        "addressOffset": 0,
        "size": 32,
        "fields": [
            "name": "value",
            "description": "",
            "bitWidth": 32,
            "bitOffset": 0,
            "reset": 0,
            "selfClear": false

The last step is to upload the generated out.json file to the airhdl web application using the Upload button in the Register Maps view:

This creates a new register map with the name that we have specified when executing the script (in this example, my_map):

The out.json file can also be used as an input to the airhdl command line generator, which can generate all the airhdl output products (VHDL and SystemVerilog RTL code, testbenches, C header and documentation) locally on the user’s machine:

java -jar airhdl-cl.jar out.json

As we have seen, all it takes is a few lines of Python code to turn a text file into an airhdl register map JSON object that can be either uploaded to the airhdl web application to create a new register map, or used locally as an input to the airhdl command line generator.

How to initialize 30,000 coefficients over AXI4 using airhdl (and a bit of custom logic)

One of our airhdl users recently asked how he could use an airhdl-generated register bank to initialize the 30,000 (or so) weights of a neural network circuit.

When dealing with such large numbers of coefficients, it’s not practical to create a dedicated addressable register for every coefficient as the corresponding logic becomes too large and and slow. What you can do instead is to either store them in a memory, which is very efficient in terms of logic resources, or in a register chain in case you need to concurrent access to the coefficients.

The memory option is quite straightforward as airhdl allows you to create memory-typed register map elements, which you can connect to a block memory components as shown in this article. The downside of this option is that you can only access one element at a time from the user logic.

In the register chain option, the coefficients are stored in the flip-flops of a large shift register, which gives you concurrent access to all of the elements. To implement the register chain, you will first need to create a write-only register (e.g. weight) with a value field corresponding to the width of your coefficients (e.g. 10 bits):

Consequently, the following user output ports are exposed in the generated RTL component:

weight_strobepulsed every time a new value is written to the weight register
weight_valuethe current value of the weight.value register field

You then connect those ports to the input of a 30,000-deep shift register that is part of the user logic, with the weight_strobe acting as a clock-enable to the shift register. To initialize the shift register and thus your coefficients, all you have to do is write the 30,000 coefficients in a row over the AXI4 interface to the weight register using a fixed address pattern. Make sure not to use an incrementing address pattern as there is really only one adressable register in the register bank.

One issue you may run into while implementing this circuit is the high fanout on the weight_strobe signal, which can cause excessive delays and thus timing violations. Here are two ideas to improve the situation:

  • Split the register chain into several smaller chain, each connected to a dedicated register.
  • Route the weight_strobe net through a global buffer like a Xilinx BUFG or BUFR. This adds a fixed delay to the net but in exchange the signal can then be distributed with low skew to a large number of cells.

We hope you will find this useful. To stay informed about what’s going on at airhdl, please follow us on twitter or on LinkedIn.

How To Integrate an airhdl Register File in Xilinx Vivado 2015.3

AirHDL is a free, web-based EDA tool for managing Xilinx ZYNQ and MicroBlaze register files. In this post, I will show you how to integrate an AirHDL-generated register file in a Xilinx ZYNQ design using Vivado 2015.3.

For the purpose of this article, I will use the “Base Zynq” example project that comes with Vivado. It’s a block diagram design of a simple Zynq processsing system with two peripherals: a GPIO and a block memory.


What we’re going to do is add a custom bus peripheral component with an AirHDL-generated register file to this design.

In Vivado, open the “Address Editor” view and look for a free address range for the register map that you’ll create in AirHDL.


I will use 0x4200_0000.

In AirHDL, create a new register map called “myMap” and make sure to use the base address you selected before (in my case: 0x4200_0000) as the regitster map’s base address:


Add a few registers to the newly created register map. I will add a read-write “control” register with a 1-bit “value” field and a read-only “status” register with also a 1-bit “value” field. In AirHDL, each register field will map to a separate port in the generated RTL component.2015-12-26_17-48-38For every register map that you create, AirHDL generates a number of different output products that you can download:

  • C Header
  • VHDL Package
  • VHDL Component
  • VHDL Instantiation Template
  • VHDL Testbench
  • HTML Documentation
  • IP-XACT Specification
  • JSON Specification

For the purpose of the integration into a Xilinx Vivado hardware design, the only files that you need are the VHDL Package and the VHDL Component. Download these and save them into an empty folder on your hard drive (e.g. e:\temp\mymap).


Back in Vivado, select Tools > Create and Package IP…

Select “Package a specified directory” and select the directory containing the downloaded AirHDL files (in my case: e:\temp\mymap).


In the automatically created IP project, check that the ports and interfaces are correct. In particular you’ll expect that the slave AXI interface and the the clock and reset signals have been detected correctly, and that the user I/Os corresponding to each register field appear in the list:


Also note that for processor-writable registers, AirHDL generates a “strobe” port that signals when the register value has been updated by the CPU.

Click “Package IP”

Back in your Vivado “Base Zynq” project, click “Add IP” and select the “myMap_regs” component:


In the block diagram editor, click “Run Connection Automation”. This will connect our component’s AXI-interface to the AXI interconnect:


Open the “Address Editor” view and update the “Offset Address” and “Range” properties of the “myMap_regs” component using the value found in the AirHDL register map view (for the range, use the next higher choice available):


In the block diagram, select the “control_value” and “status_value” ports of the “myMap_regs” component, right-click and select “Make External”. This will create top-level input and output ports and connect them to the register fields.


You’re done! You can now run the synthesis and implementation to generate the bitstream.