.. rst-class:: break_before Register Generation =================== Nomenclature ------------ There are three constructs you need to know about when working with registers: Field ~~~~~ A field is the atomic unit of a register. A field can have a minimum size of 1 and a maximum size of the register width. Every field has an associated access level that defines both hardware and software access. Register ~~~~~~~~ A register is a collection of fields. A register has an associated address that can be accessed over a connected bus (if the access policy allows it). Register Map ~~~~~~~~~~~~ A register map is a collection of registers. Basics ------ Corsair allows you to define your registers in a human-readable format (`.json` in our case) and generate all sorts of outputs from that single source of truth. Configuration File ~~~~~~~~~~~~~~~~~~ The configuration file defines how corsair behaves when creating a register map from a register file and which outputs should be generated from the register map. The global configuration section (`globcfg`) gives information about the general configuration of the corsair run. The sections below specify the generation targets, where each section defines information needed for the corresponding generator. Global Configuration Options ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +-----------------------+----------------+-----------------------------------------------------------------------------------------------------+ | Parameter | Default value | Description | +=======================+================+=====================================================================================================+ | ``base_address`` | 0 | Register map base address in global address map | +-----------------------+----------------+-----------------------------------------------------------------------------------------------------+ | ``data_width`` | 32 | Width of all data buses and registers | +-----------------------+----------------+-----------------------------------------------------------------------------------------------------+ | ``address_width`` | 16 | Address bus width (capacity of the register map) | +-----------------------+----------------+-----------------------------------------------------------------------------------------------------+ | ``register_reset`` | ``sync_pos`` | Flip-flop reset style | | | +---------------+-------------------------------------------------------------------------------------+ | | | ``sync_pos`` | Synchronous active high reset | | | +---------------+-------------------------------------------------------------------------------------+ | | | ``sync_neg`` | Synchronous active low reset | | | +---------------+-------------------------------------------------------------------------------------+ | | | ``async_pos`` | Asynchronous active high reset | | | +---------------+-------------------------------------------------------------------------------------+ | | | ``async_neg`` | Asynchronous active low reset | +-----------------------+----------------+---------------+-------------------------------------------------------------------------------------+ | ``address_increment`` | ``none`` | Address auto increment mode, if no address is provided for a register | | | +------------------------+----------------------------------------------------------------------------+ | | | ``none`` | Address auto increment mode is disabled | | | +------------------------+----------------------------------------------------------------------------+ | | | ``data_width`` | Enable address auto increment with value based on ``globcfg.data_width`` | | | +------------------------+----------------------------------------------------------------------------+ | | | ```` | Enable address auto increment with provided number of bytes, e.g 4 | +-----------------------+----------------+------------------------+----------------------------------------------------------------------------+ | ``address_alignment`` | ``data_width`` | Check for address alignment of registers. | | | +------------------------+----------------------------------------------------------------------------+ | | | ``none`` | No check of address alignment | | | +------------------------+----------------------------------------------------------------------------+ | | | ``data_width`` | Enable check of address alignment based on ``globcfg.data_width`` | | | +------------------------+----------------------------------------------------------------------------+ | | | ```` | Enable check of address alignment based on provided number of bytes, e.g 4 | +-----------------------+----------------+------------------------+----------------------------------------------------------------------------+ | ``force_name_case`` | ``none`` | Force case for all the names (registers, bit fields, enums) | | | +-----------+-----------------------------------------------------------------------------------------+ | | | ``none`` | Names used as they are | | | +-----------+-----------------------------------------------------------------------------------------+ | | | ``lower`` | Force names to have lowercase | | | +-----------+-----------------------------------------------------------------------------------------+ | | | ``upper`` | Force names to have uppercase | +-----------------------+----------------+-----------+-----------------------------------------------------------------------------------------+ A configuration file may look like this: .. code-block:: python [globcfg] base_address = 0 # register addressing starts at 0 data_width = 32 # registers are 32 bit wide address_width = 10 # 10 bit wide addresses => max # registers = 1024 register_reset = sync_neg # synchronous active-low reset address_increment = data_width # auto-increment unspecified addresses by data width (32 / 8 = 4) address_alignment = data_width # addresses must be aligned to data width (0, 4, 8, ...) force_name_case = none # do not force any naming convention regmap_path = xibif-regs.json # default register map file path [vhdl] path = src/XiBIF_regs.vhd # output file path read_filler = 0 # return '0' as data on an invalid read interface = axil # define AXI-Lite as the interface to the registers generator = Vhdl # generate the registers as a VHDL-file [py] path = ../sw/python/regs.py # output file path generator = Python # generate a Python class corresponding to the registers .. note:: You must not put comments in the actual configuration file. The ones shown here are only for documentation. .. warning:: Data widths exceeding 64 bits are not supported! Register File ~~~~~~~~~~~~~ The register file contains the actual information about the register to create. Below you can see the default register file that is provided after project creation. .. code-block:: json { "regmap": [ { "name": "version", "description": "Version Register", "address": 0, "bitfields": [ { "name": "maj", "description": "Major Version ID", "reset": 3, "width": 8, "lsb": 24, "access": "ro", "hardware": "n" }, { "name": "min", "description": "Minor Version ID", "reset": 1, "width": 8, "lsb": 16, "access": "ro", "hardware": "n" }, { "name": "patch", "description": "Patch ID", "reset": 0, "width": 8, "lsb": 8, "access": "ro", "hardware": "n" }, { "name": "rev", "description": "Revision ID", "reset": 0, "width": 8, "lsb": 0, "access": "ro", "hardware": "n" } ] }, { "name": "UUID", "description": "Unique ID", "bitfields": [ { "name": "uuid", "description": "Unique ID", "reset": 32, "width": 32, "lsb": 0, "access": "ro", "hardware": "n" } ] }, { "name": "register_1", "description": "Demo Register 1", "bitfields": [ { "name": "demo", "description": "Demo Register 1", "reset": 0, "width": 32, "lsb": 0, "access": "ro", "hardware": "i" } ] }, { "name": "register_2", "description": "Demo Register 2", "bitfields": [ { "name": "demo", "description": "Demo Register 2", "reset": 0, "width": 32, "lsb": 0, "access": "rw", "hardware": "o" } ] } ] } There are four registers being generated: `version`, `UUID`, `register_1`, and `register_2`. The `version` and the `UUID` registers are special as they will always be created regardless of whether you specify them or not. If they are not present in the register file, they will be prepended during building automatically. These registers are needed to perform some run-time checks during the start-up and connection set-up phase. The other two generated registers are read-write and read-only respectively. They serve the purpose to show you that you can specify different access permissions to the registers. .. warning:: The field ``name`` must only contain alphanumeric characters and underlines. Spaces and other characters are not supported! Use the description field to be more verbose. Generators ~~~~~~~~~~ A generator is an instance for some output that will be generated from a register map, dependent on some configuration in the configuration file. The available generators are: - Json - Yaml - Txt - Verilog - Vhdl - VerilogHeader - CHeader - SystemVerilogPackage - Markdown - Asciidoc - Latex - Python - Matlab - GuiPython (requires Gtk3) If you want to know more about the specifics of how this all works, you can come ask us. For your project, you will probably be fine with the generators that are pre-configured for you. You can modify the configuration file however you want. Register Access --------------- Addressing ~~~~~~~~~~ Since the registers in XiBIF are connected to the AXI bus, they must be compliant with how registers are addressed. The AXI bus addresses data byte-wise, meaning that a register with a width of 32 bits requires 4 bytes of address space. There are many reasons that the AXI bus is byte-addressed instead of register-addressed; the important thing is that this has to be taken into account when assigning register addresses. Luckily, there is an option in the `config` file for Corsair that checks the address alignment upon register creation. The setting is called `address_alignment` and is set to `data_width` by default. .. warning:: It may be fun to increase the register width and/or address width to insane numbers, but this will consume considerable resources on the FPGA. For convenience, the setting `address_increment` is also set to `data_width`. This means you only have to give an address to the first register in the register file, and the rest is automatically addressed correctly. If you want to manually assign addresses, you can set this to `none`. Software Access ~~~~~~~~~~~~~~~ Software access mode defines how the registers behave upon read and write operations from software. +-----------+----------------------------------------------------------------------------------------------------------------+ | Access | Description | | modes | | +===========+================================================================================================================+ | ``rw`` | Read and Write. The field can be read or written. | +-----------+----------------------------------------------------------------------------------------------------------------+ | ``rw1c`` | Read and Write 1 to Clear. The field can be read, and when 1 is written, the field is cleared. | +-----------+----------------------------------------------------------------------------------------------------------------+ | ``rw1s`` | Read and Write 1 to Set. The field can be read, and when 1 is written, the field is set. | +-----------+----------------------------------------------------------------------------------------------------------------+ | ``ro`` | Read Only. Write has no effect. | +-----------+----------------------------------------------------------------------------------------------------------------+ | ``roc`` | Read Only to Clear. The field is cleared after every read. | +-----------+----------------------------------------------------------------------------------------------------------------+ | ``roll`` | Read Only / Latch Low. The field captures a hardware active low pulse signal and is stuck at 0. The field is | | | set after every read. | +-----------+----------------------------------------------------------------------------------------------------------------+ | ``rolh`` | Read Only / Latch High. The field captures a hardware active high pulse signal and is stuck at 1. Read the | | | field to clear it. | +-----------+----------------------------------------------------------------------------------------------------------------+ | ``wo`` | Write Only. Zeros are always read. | +-----------+----------------------------------------------------------------------------------------------------------------+ | ``wosc`` | Write Only / Self Clear. The field is cleared on the next clock tick after write. | +-----------+----------------------------------------------------------------------------------------------------------------+ Hardware Access ~~~~~~~~~~~~~~~ Hardware options are used to define how the bit field will interact with HDL logic. Most of the hardware options generate inputs/outputs on the register block design, allowing for a variety of hardware interactions with your data. +-----------+-------------------------------------------------------------------------------------------------------------+ | Hardware | Description | | options | | +===========+=============================================================================================================+ | ``i`` | Input. Use input value from hardware to update the field. | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``o`` | Output. Enable output value from the field to be accessed by hardware. | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``c`` | Clear. Add signal to clear the field (fill with all zeros). | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``s`` | Set. Add signal to set the field (fill with all ones). | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``e`` | Enable. Add signal to enable the field to capture input value (must be used with ``i``). | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``l`` | Lock. Add signal to lock the field (to prevent any changes). | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``a`` | Access. Add signals to notify when bus access to the field is performed. | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``q`` | Queue. Enable queue (LIFO, FIFO) access. | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``f`` | Fixed. Enable fixed mode (field is a constant). | +-----------+-------------------------------------------------------------------------------------------------------------+ | ``n`` | None. No hardware access to the field. | +-----------+-------------------------------------------------------------------------------------------------------------+ Enumerations ------------ A bit field may contain values corresponding to specific modes/settings, called `enumerated values`, aka enums. You can assign enums to a bit field in the register configuration file. Example Register File ~~~~~~~~~~~~~~~~~~~~~ .. code-block:: json { "regmap":[ { "name": "CTRL", "description": "Control register", "address": 8, "bitfields": [ { "name": "BAUD", "description": "Baudrate value", "reset": 0, "width": 2, "lsb": 0, "access": "rw", "hardware": "o", "enums": [ { "name": "B9600", "description": "9600 baud", "value": 0 }, { "name": "B38400", "description": "38400 baud", "value": 1 }, { "name": "B115200", "description": "115200 baud", "value": 2 } ] } ] } ] } The above register configuration generates a register called `CTRL` with a single-bit field called `BAUD`. Three enumerations are defined called `B9600`, `B38400`, and `B115200`, corresponding to the register values 0, 1, and 2, respectively. .. warning:: Since the bit field is 2 bits wide, value 3 does not have an associated enumeration and should be considered invalid or `reserved`. Do not make fields that have values that should be valid but are not enumerated. In the above example, if the register containing value 3 would correspond to a baud rate of 123456 baud, but would not be written out as an enumeration, a lot of confusion could ensue. Best Practices for using Enums ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Enumerations are a great tool for improving the readability of your code, however, they also come with some pitfalls. Usually, it is best to have descriptive names for enums, such as `POWER_MODE_SLEEP` or `ADC_DISABLE`. Simply using `ON` or `OFF` is not descriptive enough. Using enums is recommended for fields that have a fixed set of possible values, for example operation modes, error codes or configuration. They should be avoided for values that do not benefit from being enumerated. For example, one `could` enumerate all integers as `1=ONE`, `2=TWO`, etc... Generating the Registers ------------------------ Once you have specified your register, you will somehow need to incorporate them into the hardware. To do this, use: .. code-block:: python xibif register --user The ``--user`` flag specifies that you only want to generate the so-called `user-registers`. There is a whole set of registers to which you do not have access that take care of many things in the background. Assuming you have not made any mistakes while typing out the register file, HDL descriptions, documentation and drivers of your registers which will be created and the registers will be automatically inserted into the existing block design. You should then be able to see the changes you have made to the registers in the block design. Depending on the options you specified, you will see more or less inputs/outputs on the register block. .. warning:: If you have the opened the block design in Vivado, do not edit it until the command has finished executing. You might need to reload the view after generation, but usually Vivado indicates this visually. Custom Generators ----------------- You might want to create an additional set of registers that do not have anything to do with the XiBIF core. For this, you can create your own generator instance in the configuration file. To enable custom generators for your XiBIF project, use the following command: .. code-block:: python xibif register --enable-custom The path to the config file is expected to be in the `hw/regs` folder of the project. To generate the custom registers, use: .. code-block:: python xibif register --custom If you wish to disable custom generators, use: .. code-block:: python xibif register --disable-custom Register Access Policies Examples --------------------------------- Software Access ~~~~~~~~~~~~~~~ rw ^^ This mode allows both reading and writing to the register field. It is commonly used for configuration registers where software needs to set and modify values dynamically, such as enabling or disabling features or adjusting parameters. rw1c ^^^^^^ A field with this mode can be read normally, but writing a `1` clears the field. This is useful for status registers where a flag should be cleared after acknowledging an event, such as clearing an interrupt request after it has been handled. rw1s ^^^^ In this mode, writing a `1` sets the field without affecting other bits, while a `0` has no effect. This is useful for setting control flags that should remain active until cleared by another mechanism. ro ^^ The register can be read but not written. This is typically used for status registers that reflect hardware state, such as sensor readings, error codes, or system status indicators. roc ^^^ This mode means the field is automatically cleared when read. It is useful for event-driven registers where reading the value acknowledges and clears the event, preventing stale data from being processed. roll ^^^^ The register captures and holds a low signal when detected, preserving its state until explicitly reset. This is often used for hardware event logging, such as detecting the occurrence of a transient fault condition. rolh ^^^^ Similar to `roll`, but latches a high signal when detected. It is commonly used in scenarios where the system must record the occurrence of an over-threshold event, such as detecting when a voltage or temperature exceeds a critical limit. wo ^^ The register can only be written to; reads always return zero or undefined data. This is typically used for control registers where writing a value triggers an action, such as starting a DMA transfer or resetting a peripheral. wosc ^^^^^^ A write operation updates the field, but it automatically clears itself on the next clock cycle. This is useful for momentary control signals, such as issuing a reset or triggering a one-shot operation without requiring manual clearing by software. Hardware Access ~~~~~~~~~~~~~~~ input ^^^^^ Use input value from hardware to update the field. This mode is used when the field should reflect a hardware-driven signal, such as sensor readings or external control signals. output ^^^^^^ Enable output value from the field to be accessed by hardware. This is typically used to expose internal register values to external hardware components. clear ^^^^^ Add signal to clear the field (fill with all zeros). Often used in control or status registers where a specific event or condition should reset the value. set ^^^ Add signal to set the field (fill with all ones). Used for flags or conditions that require explicit activation through hardware control. enable ^^^^^^ Add signal to enable the field to capture an input value (must be used with `input`). This is useful for gating mechanisms that require explicit enabling before updating. lock ^^^^ Add signal to lock the field (to prevent any changes). Used for security-sensitive or configuration registers that should not be altered after initialization. access ^^^^^^ Add signals to notify when bus access to the field is performed. Useful for tracking register accesses or triggering auxiliary logic based on register interaction. queue ^^^^^ Enable queue (LIFO, FIFO) access. Used for buffering mechanisms where multiple values need to be stored and accessed in sequence. fixed ^^^^^ Enable fixed mode (field is a constant). Typically used for hardcoded configuration values that must remain unchanged. none ^^^^ No hardware access to the field. Used when a field is meant to be software-only, with no interaction from hardware components.