Generated Output: Verilog RTL (.v)
Relevant source files
The following files were used as context for generating this wiki page:The regif tool automates the generation of Verilog RTL code for register interfaces based on a structured Excel input file. This process produces one or two Verilog files: a core register interface module (<modulename>_regif.v) and an optional wrapper module (<modulename>_regif_wrap.v) that adds field-locking capabilities. The generated code is designed to be clear, readable, and synthesizable, incorporating features like clock gating, various access types, and configurable reset strategies.
The generation logic is primarily contained within the Regif class and its helper classes (Reg, RegFields, Field) in regif.py. These classes parse the Excel data, perform validation checks, and then assemble the final Verilog output using a series of string templates.
Sources: regif.py:1450-1487, readme.md:11-12
Verilog Generation Flow
The script follows a clear flow from parsing the input to writing the final Verilog files.
This diagram shows the process of generating Verilog files from an Excel input using the regif.py script.
Sources: regif.py:1596-1614
Core Register Interface Module (<modulename>_regif.v)
This is the main file generated by the tool. It contains the complete logic for the register map, including read/write access, reset values, and connections for individual register fields.
Sources: regif.py:313-322
Module Structure
The core module is assembled from several string templates. The top-level template VFILE defines the overall structure, including module declaration, I/O ports, internal declarations, and the main write and read logic blocks.
// From VFILE template in regif.py
{defines}{macros}module {modulename}_regif(/*autoarg*/
{vioports}
);
{bmi_io}{iodeclars}
//======================================================
{vdeclars}
{write_part}
{read_part}
endmodule
{undefines}
Sources: regif.py:313-322
I/O Ports
The module features a standardized Bus Master Interface (BMI) and dynamically generated ports for each register field.
Standard BMI Ports:
The BMI_IO template defines the fixed bus interface.
| Port Name | Direction | Width | Description |
|---|---|---|---|
bmi_clk | input | 1 | Main clock for the register logic. |
bmi_rstn | input | 1 | Active-low reset. |
bmi_rd | input | 1 | Read strobe. |
bmi_wr | input | 1 | Write strobe. |
bmi_addr | input | addr_width | Address bus for selecting a register. |
bmi_wdata | input | data_width | Data bus for write operations. |
bmi_rdata | output | data_width | Data bus for read operations. |
bmi_rdvld | output | 1 | Indicates valid read data on bmi_rdata. |
bmi_clk_wr_cg | input | 1 | Optional clock gate enable for write logic. |
Sources: regif.py:270-278, regif.py:266-269
Dynamic Field Ports:
For each field defined in the Excel file, the script generates corresponding input or output ports based on its access type. For example, a Read-Only (RO) field generates an input port, while a Read-Write (RW) field generates an output port.
Sources: regif.py:1138-1234
Register Definitions
Register addresses are defined as Verilog macros for readability and easy maintenance. Each register gets a `define statement.
`define REG_NAME_A 10'h0004
`define REG_NAME_B 10'h0008
This is generated by the defines property of the Reg class.
Sources: regif.py:440-441
Write Logic
Write operations are handled within one or more always blocks, triggered by posedge bmi_clk. The logic depends on the field's access type and whether features like clock gating or protection are enabled.
The main template for write logic is W_ALWAYS_L3.
This diagram illustrates the decision flow for updating a register field upon a valid write operation.
Sources: regif.py:146-152, regif.py:734-742
Supported Access Types
The tool supports a wide range of access types, each resulting in different generated logic.
| Type | Name | Description |
|---|---|---|
RO | Read-Only | Field value is driven from an external input. Cannot be written. |
WO | Write-Only | Field can be written but not read back. Reads return 0. |
RW | Read-Write | Standard readable and writable field. |
RC | Read, Clear by HW | Readable field. HW can clear it by asserting a pulse signal. |
RWC | Read, Write-1-to-Clear | Readable field. Cleared to reset value by HW or by writing '1' to it. |
RWT | Read, Write-Toggle | Readable field. Writing '1' to a bit toggles its value. |
RWP | Read, Write-Protectable | A read-write field whose write operation is conditional. |
RWW | Read, Write-by-HW | A readable field that can be written by the bus or set by a HW signal. |
CP | Condition Protection | A field that acts as a write-condition for other fields in the same register. |
BP | Bitwise Protection | A field where each bit acts as a write-enable for the corresponding bit in another field. |
Sources: readme.md:37-55, regif.py:73-76
Read Logic
Read logic is implemented in a single always block sensitive to bmi_rd. A case statement on bmi_addr selects the appropriate register to drive onto bmi_rdata.
- Standard Read: The
R_ALWAYStemplate defines a block that combinationally selects the output based onbmi_addrduring a read cycle. - Grouped Read: If
group_sizeis configured, the read logic is pipelined, introducing a 2-cycle delay forbmi_rdvld. This is implemented using theRG_ALWAYStemplate to manage multiple smaller read blocks.
// From R_ALWAYS template in regif.py
always @(posedge bmi_clk or negedge bmi_rstn)
if(!bmi_rstn) begin
bmi_rdata <= {data_width}'b0 ;
bmi_rdvld <= 1'b0 ;
end else if(bmi_rd && ~bmi_wr) begin
case(bmi_addr)
`REG_NAME_A : begin bmi_rdvld <=1'b1; bmi_rdata <= {...} ;end
`REG_NAME_B : begin bmi_rdvld <=1'b1; bmi_rdata <= {...} ;end
default : begin bmi_rdvld <= 1'b0; bmi_rdata <= {data_width}'b0 ;end
endcase
end else begin
bmi_rdvld <= 1'b0 ;
end
Sources: regif.py:211-225, regif.py:237-254
Reset Strategy
The tool can generate code compatible with different reset strategies by using a Verilog macro. If the reset-macro option is enabled in the Excel file, the RESET_MACRO definition is prepended to the file. This allows the user to control the reset behavior (asynchronous/synchronous, active-high/low) via a global CTRL_RESET_* define.
Sources: regif.py:289-303, regif.py:1330-1331
Wrapper Module (<modulename>_regif_wrap.v)
This optional module is generated only when at least one register field has a locksig defined. It wraps the core _regif module and implements the logic to lock fields from being updated.
Sources: regif.py:1480-1487
Structure and Purpose
The wrapper instantiates the core _regif module and adds logic for fields that can be locked. The structure is defined by the WFILE template.
This diagram shows that the wrapper module contains the core _regif instance and adds a layer of locking logic controlled by external signals.
Sources: regif.py:324-351, regif.py:1422-1435
Lock Logic
The lock logic is implemented in an always block sensitive to a separate hardware clock (hw_clk, hw_rstn). When a lock_en signal is asserted, the corresponding register fields hold their current value and are not updated by the core _regif module's outputs.
The gen_lock_segment method generates this logic using the LOCK_ALWAYS_L2 template.
// From LOCK_ALWAYS_L2 template
always @(posedge hw_clk or negedge hw_rstn)
if(!hw_rstn) begin
// Reset locked fields
end else if({lock_en}) begin
// Assign locked field from its own output (hold value)
// if_name <= o_if_name;
end
Sources: regif.py:280-287, regif.py:1422-1435
Conclusion
The regif tool's Verilog generation capability provides a powerful and flexible method for creating hardware register interfaces. By translating a high-level Excel specification into synthesizable RTL, it saves significant design time, reduces the chance of manual errors, and produces clean, standardized Verilog code. The support for advanced features like field locking, clock gating, and multiple access types makes it a versatile tool for complex IP and SoC design.
Sources: readme.md:11-17