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://gitlab.com/tsfpga/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 package making it 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
from tsfpga.module import BaseModule


class Module(BaseModule):

    # This will be much nicer when the register generator supports integer fields.
    # For now it is a bit vector field.
    version = "00000011"

    def registers_hook(self):
        self.registers.add_constant(
            "version", int(self.version, base=2), f"Version number for the {self.name} module."
        )
        self.registers.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.