Default registers

Many 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 handle this, without having to duplicate names and descriptions in many places, there is a default_registers flag to the from_toml() function. Passing a list of Register objects will insert these registers first in the RegisterList.

Usage in TOML

The TOML file below is used to showcase insertion of default registers.

TOML for showcasing default registers.
 1# Adapt the "interrupt_status" default register for the needs of this register list.
 2[interrupt_status]
 3
 4# The "mode" property MUST NOT be present for a default register.
 5# The mode of the register is set in the default register object in Python.
 6
 7# The "description" property is OPTIONAL for a default register.
 8# If specified, it will only be set in this register list, not other register lists that also
 9# use this default register.
10# If not specified, the description of the default register object in Python will be used.
11description = "Interrupt status for my module."
12
13# Register fields can be added freely to a default register.
14# Will only be added to this register list, not other register lists that also use
15# this default register.
16overflow.type = "bit"
17overflow.description = "Too high data rate."
18
19underflow.type = "bit"
20underflow.description = "Too low data rate."
21
22# Note that the "interrupt_mask" default register is not adapted here.
23# It will be included in this register list at its default configuration.
24
25# Further registers and register arrays can be added freely after the default registers.
26[conf]
27
28mode = "r_w"
29description = "Generic configuration register."

Usage with Python API

The Python code below shows

  1. How to parse the TOML file listed above.

  2. How to create an identical register list when instead using the Python API.

  3. How to generate register artifacts.

Note that the result of the create_from_api call is identical to that of the parse_toml call. Meaning that using a TOML file or using the Python API is completely equivalent. You choose yourself which method you want to use in your code base.

Python code to showcase default registers.
 1import sys
 2from pathlib import Path
 3
 4from hdl_registers.generator.c.header import CHeaderGenerator
 5from hdl_registers.generator.cpp.interface import CppInterfaceGenerator
 6from hdl_registers.generator.html.page import HtmlPageGenerator
 7from hdl_registers.generator.vhdl.register_package import VhdlRegisterPackageGenerator
 8from hdl_registers.parser.toml import from_toml
 9from hdl_registers.register import Register
10from hdl_registers.register_list import RegisterList
11from hdl_registers.register_modes import REGISTER_MODES
12
13THIS_DIR = Path(__file__).parent
14
15
16DEFAULT_REGISTERS = [
17    Register(
18        name="interrupt_status",
19        index=0,
20        mode=REGISTER_MODES["r_wpulse"],
21        description="Interrupt status. Clear interrupt(s) by writing the corresponding bitmask.",
22    ),
23    Register(
24        name="interrupt_mask",
25        index=1,
26        mode=REGISTER_MODES["r_w"],
27        description="Enable or disable interrupts by setting bitmask.",
28    ),
29]
30
31
32def parse_toml() -> RegisterList:
33    """
34    Create the register list by parsing a TOML data file.
35    """
36    return from_toml(
37        name="caesar",
38        toml_file=THIS_DIR.parent / "toml" / "basic_feature_default_registers.toml",
39        default_registers=DEFAULT_REGISTERS,
40    )
41
42
43def create_from_api() -> RegisterList:
44    """
45    Alternative method: Create the register list by using the Python API.
46    """
47    register_list = RegisterList.from_default_registers(
48        name="caesar", source_definition_file=None, default_registers=DEFAULT_REGISTERS
49    )
50
51    interrupt_status = register_list.get_register(register_name="interrupt_status")
52    interrupt_status.description = "Interrupt status for my module."
53    interrupt_status.append_bit(
54        name="overflow", description="Too high data rate.", default_value="0"
55    )
56    interrupt_status.append_bit(
57        name="underflow", description="Too low data rate.", default_value="0"
58    )
59
60    register_list.append_register(
61        name="conf", mode=REGISTER_MODES["r_w"], description="Generic configuration register."
62    )
63
64    return register_list
65
66
67def generate(register_list: RegisterList, output_folder: Path) -> None:
68    """
69    Generate the artifacts that we are interested in.
70    """
71    CHeaderGenerator(register_list=register_list, output_folder=output_folder).create()
72    CppInterfaceGenerator(register_list=register_list, output_folder=output_folder).create()
73    HtmlPageGenerator(register_list=register_list, output_folder=output_folder).create()
74    VhdlRegisterPackageGenerator(register_list=register_list, output_folder=output_folder).create()
75
76
77def main(output_folder: Path) -> None:
78    generate(register_list=parse_toml(), output_folder=output_folder / "toml")
79    generate(register_list=create_from_api(), output_folder=output_folder / "api")
80
81
82if __name__ == "__main__":
83    main(output_folder=Path(sys.argv[1]))

See RegisterList.from_default_registers() for more Python API details.

Generated code

See below for a description of the code that can be generated when using register arrays.

HTML page

See HTML file below for the human-readable documentation that is produced by the generate() call in the Python example above.

See HTML generator for more details about the HTML generator and its capabilities.

HTML page

VHDL package

The VHDL code below is produced by the generate() call in the Python example above. Click the button to expand and view the code. See VHDL generator for instructions on how it can be used in your VHDL project.

Click to expand/collapse code.
Generated VHDL code.
 1-- -----------------------------------------------------------------------------
 2-- This file is automatically generated by hdl-registers version 7.3.1-dev.
 3-- Code generator VhdlRegisterPackageGenerator version 2.0.0.
 4-- Generated 2025-04-26 09:24 at Git commit e16bd7741c27.
 5-- Register hash c83f525983f77e564392d497a0e46d153aded549.
 6-- -----------------------------------------------------------------------------
 7
 8library ieee;
 9use ieee.std_logic_1164.all;
10use ieee.numeric_std.all;
11use ieee.fixed_pkg.all;
12
13library register_file;
14use register_file.register_file_pkg.all;
15
16
17package caesar_regs_pkg is
18
19  -- ---------------------------------------------------------------------------
20  -- The valid range of register indexes.
21  subtype caesar_register_range is natural range 0 to 2;
22
23  -- ---------------------------------------------------------------------------
24  -- The number of bits needed to address all 3 registers on a register bus.
25  -- Note that this figure includes the lowest two address bits that are assumed zero, since
26  -- registers are 32-bit and unaligned accesses are not supported.
27  constant caesar_address_width : positive := 4;
28
29  -- Register indexes, within the list of registers.
30  constant caesar_interrupt_status : natural := 0;
31  constant caesar_interrupt_mask : natural := 1;
32  constant caesar_conf : natural := 2;
33
34  -- Declare 'register_map' and 'regs_init' constants here but define them in
35  -- the package body (deferred constants).
36  -- So that functions have been elaborated when they are called.
37  -- Needed for ModelSim compilation to pass.
38
39  -- To be used as the 'registers' generic of 'axi_lite_register_file.vhd'.
40  constant caesar_register_map : register_definition_vec_t(caesar_register_range);
41
42  -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_register_file.vhd'.
43  subtype caesar_regs_t is register_vec_t(caesar_register_range);
44  -- To be used as the 'default_values' generic of 'axi_lite_register_file.vhd'.
45  constant caesar_regs_init : caesar_regs_t;
46
47  -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_register_file.vhd'.
48  subtype caesar_reg_was_accessed_t is std_ulogic_vector(caesar_register_range);
49
50  -- -----------------------------------------------------------------------------
51  -- Fields in the 'interrupt_status' register.
52  -- Range of the 'overflow' field.
53  constant caesar_interrupt_status_overflow : natural := 0;
54  -- Default value of the 'overflow' field.
55  constant caesar_interrupt_status_overflow_init : std_ulogic := '0';
56
57  -- Range of the 'underflow' field.
58  constant caesar_interrupt_status_underflow : natural := 1;
59  -- Default value of the 'underflow' field.
60  constant caesar_interrupt_status_underflow_init : std_ulogic := '0';
61
62end package;
63
64package body caesar_regs_pkg is
65
66  constant caesar_register_map : register_definition_vec_t(caesar_register_range) := (
67    0 => (index => caesar_interrupt_status, mode => r_wpulse, utilized_width => 2),
68    1 => (index => caesar_interrupt_mask, mode => r_w, utilized_width => 32),
69    2 => (index => caesar_conf, mode => r_w, utilized_width => 32)
70  );
71
72  constant caesar_regs_init : caesar_regs_t := (
73    0 => "00000000000000000000000000000000",
74    1 => "00000000000000000000000000000000",
75    2 => "00000000000000000000000000000000"
76  );
77
78end package body;

C++ interface header

The C++ interface header code below is produced by the generate() call in the Python example above. Click the button to expand and view the code.

Click to expand/collapse code.
Generated C++ interface class code.
  1// -----------------------------------------------------------------------------
  2// This file is automatically generated by hdl-registers version 7.3.1-dev.
  3// Code generator CppInterfaceGenerator version 1.0.0.
  4// Generated 2025-04-26 09:24 at Git commit e16bd7741c27.
  5// Register hash c83f525983f77e564392d497a0e46d153aded549.
  6// -----------------------------------------------------------------------------
  7
  8#pragma once
  9
 10#include <sstream>
 11#include <cstdint>
 12#include <cstdlib>
 13
 14namespace fpga_regs
 15{
 16
 17  namespace caesar
 18  {
 19
 20    // Attributes for the 'interrupt_status' register.
 21    namespace interrupt_status {
 22      // Attributes for the 'overflow' field.
 23      namespace overflow
 24      {
 25        // The number of bits that the field occupies.
 26        static const size_t width = 1;
 27        // The bit index of the lowest bit in the field.
 28        static const size_t shift = 0;
 29        // The bit mask of the field, at index zero.
 30        static const uint32_t mask_at_base = (1uLL << width) - 1;
 31        // The bit mask of the field, at the field's bit index.
 32        static const uint32_t mask_shifted = mask_at_base << shift;
 33
 34        // Initial value of the field at device startup/reset.
 35        static const bool default_value = false;
 36        // Raw representation of the initial value, at the the field's bit index.
 37        static const uint32_t default_value_raw = 0 << shift;
 38      }
 39
 40      // Attributes for the 'underflow' field.
 41      namespace underflow
 42      {
 43        // The number of bits that the field occupies.
 44        static const size_t width = 1;
 45        // The bit index of the lowest bit in the field.
 46        static const size_t shift = 1;
 47        // The bit mask of the field, at index zero.
 48        static const uint32_t mask_at_base = (1uLL << width) - 1;
 49        // The bit mask of the field, at the field's bit index.
 50        static const uint32_t mask_shifted = mask_at_base << shift;
 51
 52        // Initial value of the field at device startup/reset.
 53        static const bool default_value = false;
 54        // Raw representation of the initial value, at the the field's bit index.
 55        static const uint32_t default_value_raw = 0 << shift;
 56      }
 57
 58      // Struct that holds the value of each field in the register,
 59      // in a native C++ representation.
 60      struct Value {
 61        bool overflow;
 62        bool underflow;
 63      };
 64      // Initial value of the register at device startup/reset.
 65      const Value default_value = {
 66        overflow::default_value,
 67        underflow::default_value,
 68      };
 69    }
 70
 71  } // namespace caesar
 72
 73  class ICaesar
 74  {
 75  public:
 76    // Number of registers within this register list.
 77    static const size_t num_registers = 3uL;
 78
 79    virtual ~ICaesar() {}
 80
 81    // -------------------------------------------------------------------------
 82    // Methods for the 'interrupt_status' register.
 83    // Mode 'Read, Write-pulse'.
 84    // -------------------------------------------------------------------------
 85    // Read the whole register value over the register bus.
 86    virtual caesar::interrupt_status::Value get_interrupt_status() const = 0;
 87
 88    // Read the whole register value over the register bus.
 89    // This method returns the raw register value without any type conversion.
 90    virtual uint32_t get_interrupt_status_raw() const = 0;
 91
 92    // Read the register and slice out the 'overflow' field value.
 93    virtual bool get_interrupt_status_overflow() const = 0;
 94
 95    // Read the register and slice out the 'underflow' field value.
 96    virtual bool get_interrupt_status_underflow() const = 0;
 97
 98    // Write the whole register value over the register bus.
 99    virtual void set_interrupt_status(
100      caesar::interrupt_status::Value register_value
101    ) const = 0;
102
103    // Write the whole register value over the register bus.
104    // This method takes a raw register value and does not perform any type conversion.
105    virtual void set_interrupt_status_raw(
106      uint32_t register_value
107    ) const = 0;
108
109    // Write the 'overflow' field value.
110    // Will write the register with all other fields set as default.
111    virtual void set_interrupt_status_overflow(
112      bool field_value
113    ) const = 0;
114
115    // Write the 'underflow' field value.
116    // Will write the register with all other fields set as default.
117    virtual void set_interrupt_status_underflow(
118      bool field_value
119    ) const = 0;
120    // -------------------------------------------------------------------------
121
122    // -------------------------------------------------------------------------
123    // Methods for the 'interrupt_mask' register.
124    // Mode 'Read, Write'.
125    // -------------------------------------------------------------------------
126    // Read the whole register value over the register bus.
127    virtual uint32_t get_interrupt_mask() const = 0;
128
129    // Write the whole register value over the register bus.
130    virtual void set_interrupt_mask(
131      uint32_t register_value
132    ) const = 0;
133    // -------------------------------------------------------------------------
134
135    // -------------------------------------------------------------------------
136    // Methods for the 'conf' register.
137    // Mode 'Read, Write'.
138    // -------------------------------------------------------------------------
139    // Read the whole register value over the register bus.
140    virtual uint32_t get_conf() const = 0;
141
142    // Write the whole register value over the register bus.
143    virtual void set_conf(
144      uint32_t register_value
145    ) const = 0;
146    // -------------------------------------------------------------------------
147  };
148
149} // namespace fpga_regs

C header

The C code below is produced by the generate() call in the Python example above. Click the button to expand and view the code.

Click to expand/collapse code.
Generated C code.
 1// -----------------------------------------------------------------------------
 2// This file is automatically generated by hdl-registers version 7.3.1-dev.
 3// Code generator CHeaderGenerator version 1.0.0.
 4// Generated 2025-04-26 09:24 at Git commit e16bd7741c27.
 5// Register hash c83f525983f77e564392d497a0e46d153aded549.
 6// -----------------------------------------------------------------------------
 7
 8#ifndef CAESAR_REGS_H
 9#define CAESAR_REGS_H
10
11
12// Number of registers within this register list.
13#define CAESAR_NUM_REGS (3u)
14
15// Type for this register list.
16typedef struct caesar_regs_t
17{
18  // Mode "Read, Write-pulse".
19  uint32_t interrupt_status;
20  // Mode "Read, Write".
21  uint32_t interrupt_mask;
22  // Mode "Read, Write".
23  uint32_t conf;
24} caesar_regs_t;
25
26// Address of the 'interrupt_status' register.
27// Mode 'Read, Write-pulse'.
28#define CAESAR_INTERRUPT_STATUS_INDEX (0u)
29#define CAESAR_INTERRUPT_STATUS_ADDR (4u * CAESAR_INTERRUPT_STATUS_INDEX)
30// Attributes for the 'overflow' field in the 'interrupt_status' register.
31#define CAESAR_INTERRUPT_STATUS_OVERFLOW_SHIFT (0u)
32#define CAESAR_INTERRUPT_STATUS_OVERFLOW_MASK (0b1u << 0u)
33#define CAESAR_INTERRUPT_STATUS_OVERFLOW_MASK_INVERSE (~CAESAR_INTERRUPT_STATUS_OVERFLOW_MASK)
34// Attributes for the 'underflow' field in the 'interrupt_status' register.
35#define CAESAR_INTERRUPT_STATUS_UNDERFLOW_SHIFT (1u)
36#define CAESAR_INTERRUPT_STATUS_UNDERFLOW_MASK (0b1u << 1u)
37#define CAESAR_INTERRUPT_STATUS_UNDERFLOW_MASK_INVERSE (~CAESAR_INTERRUPT_STATUS_UNDERFLOW_MASK)
38
39// Address of the 'interrupt_mask' register.
40// Mode 'Read, Write'.
41#define CAESAR_INTERRUPT_MASK_INDEX (1u)
42#define CAESAR_INTERRUPT_MASK_ADDR (4u * CAESAR_INTERRUPT_MASK_INDEX)
43
44// Address of the 'conf' register.
45// Mode 'Read, Write'.
46#define CAESAR_CONF_INDEX (2u)
47#define CAESAR_CONF_ADDR (4u * CAESAR_CONF_INDEX)
48
49#endif // CAESAR_REGS_H