A Verilog/SystemVerilog code formatter for VS Code with granular control over formatting features.
Features (More to be added)
This formatter provides comprehensive formatting capabilities with individual control over each feature:
- Module header formatting with port alignment
- Assignment alignment
- Wire/reg/logic declaration alignment
- Parameter/localparam alignment
- Module instantiation formatting
- Always/initial block indentation
- Case statement indentation
- If/else block begin/end enforcement
- For loop begin/end enforcement
- Generate block indentation
- Comment column alignment
- Ifdef/else/endif annotation
- Trailing whitespace removal
- Blank line compression
Usage
- Keyboard:
Shift+Alt+F (Windows/Linux) or Shift+Option+F (macOS)
- Context Menu: Right-click → "Format Document"
- Command Palette:
Ctrl+Shift+P → "Format Document"
- Keyboard:
Shift+Alt+F (Windows/Linux) or Shift+Option+F (macOS) with text selected
- Context Menu: Right-click on selected text → "Format Selection"
- Command Palette:
Ctrl+Shift+P → "Format Selection"
Recommended Keybindings
For the best experience, add these keybindings to your keybindings.json (File → Preferences → Keyboard Shortcuts → Open Keyboard Shortcuts JSON):
{
"key": "shift+alt+f",
"command": "editor.action.formatDocument",
"when": "editorTextFocus && !editorHasSelection"
},
{
"key": "shift+alt+f",
"command": "editor.action.formatSelection",
"when": "editorTextFocus && editorHasSelection"
}
This allows Shift+Alt+F to automatically format the entire document when nothing is selected, or format just the selection when text is selected.
Add to your settings.json:
{
"[verilog]": {
"editor.defaultFormatter": "internal.verigood-verilog-formatter",
"editor.formatOnSave": true
},
"[systemverilog]": {
"editor.defaultFormatter": "internal.verigood-verilog-formatter",
"editor.formatOnSave": true
}
}
Configuration
All Available Settings
{
"verilogFormatter.indentSize": 2,
"verilogFormatter.maxBlankLines": 1,
"verilogFormatter.alignPortList": true,
"verilogFormatter.alignParameters": true,
"verilogFormatter.wrapPortList": true,
"verilogFormatter.lineLength": 160,
"verilogFormatter.removeTrailingWhitespace": true,
"verilogFormatter.alignAssignments": true,
"verilogFormatter.alignWireDeclSemicolons": true,
"verilogFormatter.commentColumn": 40,
"verilogFormatter.formatModuleInstantiations": true,
"verilogFormatter.formatModuleHeaders": true,
"verilogFormatter.indentAlwaysBlocks": true,
"verilogFormatter.enforceBeginEnd": true,
"verilogFormatter.indentCaseStatements": true,
"verilogFormatter.annotateIfdefComments": true
}
Notes
How Module Instantiation is Detected
The formatter recognizes module instantiations using the following patterns:
Module with Parameters: Detects module_name #( to start parameter parsing
my_module #(
.PARAM1(value1),
.PARAM2(value2)
) instance_name (
Direct Port Instantiation: Detects module_name instance_name( to start port parsing
my_module instance_name (
.port1(signal1),
.port2(signal2)
);
Instance Name with Array Range (v1.3.2+): Supports array-indexed instances
my_module instance_name[7:0] (
.port(signal)
);
DWC_module instance[(P_NR-1):0] (
.clk(clk),
.data(data)
);
Pattern matched: instance_name[range] where range can include parameters, numbers, and expressions.
Transition from Parameters to Ports: Detects ) instance_name( after parameter block
my_module #(
.WIDTH(8)
) inst_name ( // ← Detected here
.data(bus)
);
How Port and Parameter Concatenations are Detected
The formatter identifies concatenations using parenthesis balance checking:
Port Concatenations: Detects .port_name({ pattern
.port_name({signal1, signal2, 8'd0})
The formatter counts opening ( and closing ) to determine if the concatenation is:
- Single-line: Balanced parentheses on the same line → formatted as-is
- Multi-line: Unbalanced parentheses → split across multiple lines with alignment
Parameter Concatenations: Detects .PARAM_NAME({ pattern
.DATA_WIDTH({8, 16, 32})
Multiline Detection: Before attempting single-line formatting, the formatter:
- Counts all
( characters in the line
- Counts all
) characters in the line
- If parentheses are balanced (equal count and closing paren found), treats as single-line
- If unbalanced, treats as multi-line and continues parsing subsequent lines
Example - Unbalanced (Multi-line):
.P_WL((P_L2_RXCBS_PS*P_P_NR_p1) // ← 2 open, 1 close = unbalanced
+ LP_NEWRXCB_PARITY_W ) // ← Closing paren on next line
Example - Balanced (Single-line):
.P_WL((P_L2_RXCBS_PS*P_P_NR_p1)) // ← 2 open, 2 close = balanced
Supported Signal Patterns
Within port/parameter connections, the formatter recognizes:
- Simple identifiers:
signal_name, clk, rst_n
- Indexed signals:
data[7:0], array[i], bus[WIDTH-1:0]
- Hierarchical references:
module.signal, top.sub.wire
- Numeric literals:
8'd0, 16'hFFFF, 1'b1
- Expressions:
count + 1, WIDTH * 2, {MSB, LSB}
- Concatenations:
{a, b, c}, {8{1'b0}}
License
MIT License - See LICENSE for details.