Integration with hdl-registers

The tsfpga module and source code handling is tightly integrated with its sister project hdl-registers (https://hdl-registers.com, https://github.com/hdl-registers/hdl-registers), a register code generator. To use it simply create a file regs_<name>.toml in the root of a module (see module structure). It is fast enough that before each build and each simulation run, the modules will re-generate their VHDL register artifacts making them always up-to-date. Creating documentation and headers, which are typically distributed as part of FPGA release artifacts, is simple and easy to integrate in a build script.

Releases to PyPI of tsfpga list hdl-registers as a dependency, so it will be installed as well.

Example usage in tsfpga

The tsfpga/examples/modules/ddr_buffer example module is heavily reliant on generated register information, both in the implementation and testbench.

Default registers

A lot of projects use a few default registers in standard locations that shall be present in all modules. For example, very commonly the first register of a module is an interrupt status register and the second one is an interrupt mask. In order to achieve this, without having to duplicate names and descriptions in many places, there is a default_registers flag to the get_modules() function. Passing a list of hdl_registers.register.Register objects will insert them in the register list of all modules that use registers.

Manipulating registers from Python

The ddr_buffer example module also showcases how to manipulate registers from Python via tsfpga’s module system. This method for manipulating registers can be very useful for information that is known in the Python realm, but is not convenient to add to the TOML file.

module_ddr_buffer.py
# First party libraries
from tsfpga.module import BaseModule


class Module(BaseModule):
    version = 3

    def registers_hook(self):
        # Should have some registers already from the TOML file.
        register_list = self.registers
        assert register_list is not None

        register_list.add_constant(
            "version", self.version, f"Version number for the {self.name} module."
        )
        register_list.get_register("version").get_field("version").default_value = self.version

Using BaseModule.registers_hook() we add a constant as well as a read-only register for the module’s version number. The idea behind this example is that a software that uses this module will read the version register and compare to the static constant that shows up in the header. This will make sure that the software is running against the correct FPGA with expected module version.

Choosing what artifacts to generate

Per default, the module will generate all register VHDL artifacts. Which includes register packages, AXI-Lite register file wrapper, and simulation support packages. The easiest way to disable either of these is to set up a module_foo.py in the root of your module and disable either of the class variables below. They all have default value True in BaseModule.