AUTO_TEMPLATE
This document describes the AUTO_TEMPLATE system for customizing how ports are connected during AUTOINST expansion. Templates provide powerful pattern matching and signal transformation capabilities.
Introduction
When slang-autos expands /*AUTOINST*/, it connects each port to a signal
with the same name by default. AUTO_TEMPLATE lets you override this behavior
to:
Rename signals based on instance names (e.g.,
data→fifo_0_data)Transform port names (e.g.,
axi_wdata→m_axi_wdata)Tie off unused ports to constants
Leave specific ports unconnected
Compute signal names using math expressions
Templates are especially useful when instantiating multiple copies of a module where each instance needs different signal names.
Template Syntax
Basic Structure
A template is defined in a block comment immediately before one or more module instantiations:
/* module_name AUTO_TEMPLATE ["instance_pattern"]
port_pattern => signal_expression
port_pattern => signal_expression
*/
module_name instance_name (/*AUTOINST*/);
Components:
module_name: The module type this template applies to (required)
instance_pattern: Optional regex to match instance names and extract captures
port_pattern: Port name or regex pattern to match
signal_expression: The signal name or expression to connect
Rule Ordering
Rules are evaluated in order, and the first matching rule wins. This means more specific patterns should come before general catch-all patterns:
/* fifo AUTO_TEMPLATE
clk => sys_clk
rst_n => sys_rst_n
data_.* => fifo_$0
.* => $0
*/
In this example, clk and rst_n get specific mappings, ports starting
with data_ get prefixed, and everything else passes through unchanged.
Instance Pattern Matching
The instance pattern is an optional regex that matches against instance names and extracts capture groups for use in signal expressions.
Default Behavior
When no instance pattern is specified, slang-autos automatically extracts the first number found in the instance name:
/* fifo AUTO_TEMPLATE
din => fifo_@_din
*/
fifo u_fifo_0 (/*AUTOINST*/); // @ = "0", connects to fifo_0_din
fifo u_fifo_1 (/*AUTOINST*/); // @ = "1", connects to fifo_1_din
This works with numbers anywhere in the name:
u_fifo_0→ extracts0fifo3_inst→ extracts3ms2m_adapter→ extracts2
Custom Instance Patterns
For more control, specify a regex pattern in quotes:
/* fifo AUTO_TEMPLATE "u_(.+)_fifo_(\d+)"
din => %1_data_in[%2]
*/
fifo u_tx_fifo_0 (/*AUTOINST*/); // %1="tx", %2="0" → tx_data_in[0]
fifo u_rx_fifo_3 (/*AUTOINST*/); // %1="rx", %2="3" → rx_data_in[3]
Instance Capture Variables
%1,%2,%3, … - Capture groups from instance pattern%{1},%{2}, … - Brace variant (use when followed by a digit)%0or%{0}- Full instance name@- Alias for%1(verilog-mode compatibility)
The brace variant is necessary when the variable is followed by a digit:
/* mem AUTO_TEMPLATE "bank_(\d+)"
addr => bank%{1}0_addr
*/
mem bank_2 (/*AUTOINST*/); // Connects to bank20_addr (not bank_addr with "20")
Port Pattern Matching
Port patterns can be literal names or regex patterns that match against port names and extract capture groups.
Literal Matching
Simple port names are matched exactly:
/* module AUTO_TEMPLATE
clk => sys_clk
rst_n => sys_rst_n
*/
Regex Patterns
Use regex for flexible matching:
/* axi_slave AUTO_TEMPLATE
axi_(.*) => s_axi_$1
data_([io])n => port_$1
*/
Common patterns:
.*- Match any port (catch-all)data_.*- Match ports starting withdata_(.*)_valid- Match ports ending with_valid, capture prefix([^_]+)_(.+)- Split on first underscore
Port Capture Variables
$1,$2,$3, … - Capture groups from port pattern${1},${2}, … - Brace variant$0or${0}- Full port name (equivalent toport.name)
Substitution Variables
Signal expressions can include various substitution variables that are replaced during expansion.
Instance Captures
From the instance pattern regex:
Variable |
Description |
|---|---|
|
Capture groups from instance pattern |
|
Brace variant (use when followed by digit) |
|
Full instance name |
|
Alias for |
Port Captures
From the port pattern regex:
Variable |
Description |
|---|---|
|
Capture groups from port pattern |
|
Brace variant |
|
Full port name |
Port Properties
Access port metadata:
Variable |
Description |
|---|---|
|
Port name (same as |
|
Bit width as integer (e.g., |
|
Range string (e.g., |
|
Direction string: |
|
|
|
|
|
|
Instance Properties
Variable |
Description |
|---|---|
|
Full instance name |
Special Values
These special values have specific meanings in signal expressions:
Unconnected Ports
Use _ (underscore) to leave a port unconnected:
/* debug_module AUTO_TEMPLATE
debug_.* => _
*/
This generates an empty connection: .debug_port()
Constant Values
Tie ports to constant values:
Value |
Description |
|---|---|
|
Logic zero (unsized) |
|
Logic one (unsized) |
|
High impedance / tri-state |
Example:
/* unused_module AUTO_TEMPLATE
enable => '1
data => '0
*/
Warning
Assigning constants to output ports generates a warning, as this is usually unintentional. Use ternary expressions to handle bidirectional cases.
Ternary Expressions
Ternary expressions allow conditional signal assignment based on port properties.
Syntax
condition ? true_value : false_value
The condition must evaluate to "0" or "1" after variable substitution.
This is typically used with the port direction booleans.
Direction-Based Assignment
Connect different signals based on port direction:
/* bidir_module AUTO_TEMPLATE
data => port.input ? data_in : data_out
*/
Tie-Off Patterns
A common pattern is to tie inputs to a constant while leaving outputs unconnected:
/* unused_module AUTO_TEMPLATE
.* => port.input ? '0 : _
*/
Or vice versa - only connect outputs:
/* monitor AUTO_TEMPLATE
.* => port.output ? mon_$0 : _
*/
Nested Ternaries
For more complex logic, ternaries can be nested:
/* complex AUTO_TEMPLATE
.* => port.input ? input_bus : port.output ? output_bus : bidir_bus
*/
Math Functions
Math functions allow computing signal names or indices from numeric values.
Available Functions
Function |
Description |
|---|---|
|
Addition: a + b |
|
Subtraction: a - b |
|
Multiplication: a * b |
|
Integer division: a / b |
|
Modulo: a % b |
Arguments must be integers after variable substitution. Negative results are supported.
Note
Division or modulo by zero returns 0 and generates a warning.
Basic Usage
Offset instance numbers:
/* stage AUTO_TEMPLATE "stage_(\d+)"
prev_data => stage_sub(@, 1)_data
next_data => stage_add(@, 1)_data
*/
stage stage_5 (/*AUTOINST*/);
// prev_data → stage_4_data
// next_data → stage_6_data
Nested Functions
Functions can be nested for complex calculations:
/* ring AUTO_TEMPLATE "node_(\d+)"
next => node_mod(add(@, 1), 4)_port
*/
ring node_0 (/*AUTOINST*/); // next → node_1_port
ring node_3 (/*AUTOINST*/); // next → node_0_port (wraps around)
Array Indexing
Compute array indices from instance numbers:
/* slice AUTO_TEMPLATE "slice_(\d+)"
data => bus_data[add(mul(@, 8), 7):mul(@, 8)]
*/
slice slice_0 (/*AUTOINST*/); // data → bus_data[7:0]
slice slice_1 (/*AUTOINST*/); // data → bus_data[15:8]
slice slice_2 (/*AUTOINST*/); // data → bus_data[23:16]
Practical Examples
Basic Instance Numbering
Multiple FIFOs with numbered signals:
/* fifo AUTO_TEMPLATE "u_fifo_(\d+)"
wr_data => fifo_@_wr_data
rd_data => fifo_@_rd_data
wr_en => fifo_@_push
rd_en => fifo_@_pop
full => fifo_@_full
empty => fifo_@_empty
*/
fifo u_fifo_0 (/*AUTOINST*/);
fifo u_fifo_1 (/*AUTOINST*/);
fifo u_fifo_2 (/*AUTOINST*/);
Port Prefix Transformation
Add or change signal prefixes:
/* axi_master AUTO_TEMPLATE
m_axi_(.*) => axi_$1
clk => axi_clk
rst_n => axi_rst_n
*/
AXI Interface Remapping
Remap AXI signals between naming conventions:
/* axi_slave AUTO_TEMPLATE
s_axi_aw(.*) => axi_aw$1
s_axi_w(.*) => axi_w$1
s_axi_b(.*) => axi_b$1
s_axi_ar(.*) => axi_ar$1
s_axi_r(.*) => axi_r$1
s_axi_(.*) => axi_$1
*/
Multiple Capture Groups
Extract multiple parts from port names:
/* crossbar AUTO_TEMPLATE "xbar_(\d+)"
port_(\d+)_(.*) => node_%1_p$1_$2
*/
crossbar xbar_0 (/*AUTOINST*/);
// port_0_valid → node_0_p0_valid
// port_1_data → node_0_p1_data
Unused Module Tie-Off
Completely tie off an unused module:
/* debug_controller AUTO_TEMPLATE
.* => port.input ? '0 : _
*/
debug_controller u_debug (/*AUTOINST*/);
Ring Buffer Connections
Connect modules in a ring topology:
/* ring_node AUTO_TEMPLATE "node_(\d+)"
to_next => link_mod(add(@, 1), 8)
from_prev => link_@
*/
ring_node node_0 (/*AUTOINST*/); // to_next→link_1, from_prev→link_0
ring_node node_7 (/*AUTOINST*/); // to_next→link_0, from_prev→link_7
Diagnostics and Troubleshooting
Common Warnings
Unresolved variable
Warning: Unresolved substitution variable '$2' in template
This occurs when referencing a capture group that doesn’t exist. Check that your regex has enough capture groups.
Invalid regex pattern
Error: Invalid regex pattern in template
The regex syntax is invalid. slang-autos uses std::regex (ECMAScript
syntax), which differs slightly from Perl-compatible regex. An invalid regex in
the instance pattern is a hard error and the whole template is rejected; an
invalid regex in a port pattern is a warning and that rule is skipped.
Malformed template
Error: AUTO_TEMPLATE ... rejected
Templates are parsed strictly. The following are hard errors (the entire template block is rejected):
Legacy
verilog-mode-style wrapper parens around the template body (AUTO_TEMPLATE(...)).Stray trailing
)on rule signals (these used to be silently stripped, which would have broken expressions likeadd(@, 1)).Invalid regex in the instance pattern (as above).
Constant assigned to output
Warning: Assigning constant to output port 'data_out'
You’re assigning '0, '1, or 'z to an output port. This is usually
a mistake. Use a ternary expression if you need direction-aware behavior.
Common Mistakes
Forgetting braces around variables followed by digits
/* Wrong - %10 is interpreted as capture group 10 */
mem_addr => bank%10_addr
/* Correct - use braces */
mem_addr => bank%{1}0_addr
Order of rules matters
/* Wrong - catch-all first, specific rules never match */
/* module AUTO_TEMPLATE
.* => sig_$0
clk => sys_clk
*/
/* Correct - specific rules first */
/* module AUTO_TEMPLATE
clk => sys_clk
.* => sig_$0
*/
Regex special characters need escaping
/* Wrong - dot matches any character */
data.in => signal
/* Correct - escape the dot for literal match */
data\.in => signal
/* Or use it intentionally as wildcard */
data.* => prefix_$0