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.
# 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
.
create_register_package
, controls whetherVhdlRegisterPackageGenerator
is generated into the register source folder of the module.create_record_package
, controls whetherVhdlRecordPackageGenerator
is generated into the register source folder of the module.create_axi_lite_wrapper
, controls whetherVhdlAxiLiteWrapperGenerator
is generated into the register source folder of the module.create_simulation_read_write_package
, controls whetherVhdlSimulationReadWritePackageGenerator
is generated into the register simulation folder of the module.create_simulation_check_package
, controls whetherVhdlSimulationCheckPackageGenerator
is generated into the register simulation folder of the module.create_simulation_wait_until_package
, controls whetherVhdlSimulationWaitUntilPackageGenerator
is generated into the register simulation folder of the module.