Advanced Feature: Lockable Registers
Relevant source files
The following files were used as context for generating this wiki page:The regif tool provides an advanced feature to generate Verilog for lockable registers. This allows specific register fields to be write-protected based on the state of an external lock_sig signal. When a register field is locked, its value is held and cannot be updated via the standard register write interface. This is particularly useful for protecting critical configuration registers from accidental or unauthorized changes after initial setup.
The implementation generates a wrapper Verilog module (<module_name>_regif_wrap.v) around the core register interface module. This wrapper contains the locking logic, which conditionally updates the register values based on the lock signal. This architectural separation keeps the core register logic clean while providing system-level control.
Sources: readme.md:41, regif.py:328-347, 1318-1335
Configuration
To make a register field lockable, you must specify a signal name in the locksig column for that field in the input Excel file.
Sources: regif.py:73, 910-919
Excel Field Definition
The locksig field is defined as part of the register field format.
| Parameter | Regex Match | Example | Description |
|---|---|---|---|
locksig | `[a-zA-Z]\w*\Z | \A\Z` | cfg_lock |
Sources: regif.py:73
Non-Lockable Field Types
Certain register field types are inherently not designed to be lockable. If a locksig is provided for these types, it will be ignored.
The following field types cannot be locked:
RO(Read-Only)RC(Read-to-Clear)RWT(Read-Write-Toggle)RWP(Read-Write-Pulse)CP(Condition Protection)BP(Bitwise Protection)
Sources: regif.py:912, 1297-1304
Architecture
The lockable register feature is implemented using a two-module Verilog architecture: a core _regif module and a _regif_wrap module.
This diagram shows that the wrapper module (_regif_wrap.v) contains the core register instance (u_<module_name>_regif) and the locking logic. The external system interacts with the wrapper.
Sources: regif.py:328-347, 1318-1335
Core Module (<module_name>_regif.v)
This module contains the standard register read/write logic without any knowledge of the locking mechanism. It exposes the register field values as outputs.
Sources: regif.py:307-326
Wrapper Module (<module_name>_regif_wrap.v)
This module instantiates the core module and implements the locking behavior.
- It takes the
lock_sig(e.g.,lock_en) as an input. - It instantiates the core
_regifmodule. - It contains an
alwaysblock sensitive to a hardware clock (hw_clk) and reset (hw_rstn). - When the
lock_sigis asserted, thisalwaysblock drives the outputs of the wrapper with the current (locked) values. When de-asserted, it passes through the values from the core_regifmodule.
Sources: regif.py:328-347, 1318-1335
Implementation Details
The Python script processes the Excel file and generates the necessary Verilog structures for the locking mechanism.
Identifying Lockable Fields
The Regif.getlockdict() method scans all register fields and creates a dictionary where keys are the lock_sig names and values are lists of field objects to be controlled by that signal.
# regif.py:1295-1304
def getlockdict(self):
_lockdict = {}
for reg in self.reg_objs:
for fd in reg.fields.fields:
if (fd.lock_sig and fd.type not in self.unlocktype):
if fd.lock_sig not in _lockdict:
_lockdict[fd.lock_sig] = [fd]
elif not reg.fifo_if:
_lockdict[fd.lock_sig].append(fd)
return _lockdict
Sources: regif.py:1295-1304
Generating the Locking Logic
The Regif.gen_lock_segment() method uses the dictionary from getlockdict() to generate the Verilog code for the always block that implements the lock.
The process flow for generating the lock logic is as follows:
Sources: regif.py:1306-1317
The core of the generated logic comes from the LOCK_ALWAYS_L2 template:
# regif.py:298-305
LOCK_ALWAYS_L2 = \
'''
always @(posedge hw_clk or negedge hw_rstn)
if(!hw_rstn) begin{lock_rsts}
end else if({lock_en}) begin
{lock_express} end
'''
{lock_rsts}is populated with the reset values for the locked fields.{lock_en}is thelock_sigfrom the Excel file.{lock_express}contains the logic to pass through the unlocked values whenlock_enis low.
Sources: regif.py:298-305, 1306-1317
Generating the Wrapper Module
The final wrapper file is created using the WFILE template, which includes placeholders for the lock I/O ({lock_io}) and the locking logic itself ({lock_part}).
# regif.py:328-347
WFILE = \
'''module {modulename}_regif_wrap(/*autoarg*/
{wioports}
);
input hw_clk ;
input hw_rstn ;
{bmi_io}{lock_io}{iodeclars}
//======================================================
wire [{data_widthp1:2}:0] bmi_rdata ;
wire bmi_rdvld ;
{wdeclars}
//======================================================
{modulename}_regif u_{modulename}_regif(
...
{instports}
);
//======================================================
{lock_part}
endmodule
'''
The gen_lock_segment function provides the content for {lock_io} (the input declaration for the lock signal) and {lock_part} (the always block).
Sources: regif.py:328-347, 1306-1317
Summary
The lockable registers feature in regif provides a powerful and automated way to protect critical hardware registers. By simply defining a locksig in an Excel sheet, the tool generates a clean, two-module Verilog architecture that separates the core register logic from the system-level locking control. This enhances design robustness by preventing unintended modifications to sensitive configurations during runtime.
Sources: regif.py:1295-1335, readme.md:41