Verilog RTL Connector
A VS Code extension that lets you visually interconnect Verilog / SystemVerilog modules and
auto-generate a top-level wrapper with correct port lists, internal wire declarations and
instance port-maps. Think of it as a lightweight, open-source alternative to the (closed-source)
verilogCCN connector — built so you can read, extend and own the code.
New here? Read the user manual (中文使用手册) in MANUAL.md — quick start, every command
in detail, the full rule tables of the RTL Code Check, a command cheat-sheet and the FAQ.
Features
Workspace scan — parses every *.v / *.sv file and extracts module name, parameters,
port names, directions (input/output/inout) and bus widths ([7:0]).
Explorer-style module tree — the Modules panel is an Explorer-style tree (folder > file >
module), with chevrons to expand/collapse and a + button on each module to add it to the canvas.
Working panel — a second sidebar to the right of Modules that lists every module currently
on the canvas, with a × button to remove it. Both sidebars are independently resizable.
Visual canvas — click a module's + to drop it on the canvas as a draggable card with a
header showing the module name and its source file path, and port dots on its left (inputs) /
right (outputs). Drag the card header to move a module.
Drag-to-connect — drag from one port dot onto another to draw an orthogonal wire. Direction
compatibility is checked (output → input; inout / unknown connect freely).
Auto Connect — wires ports across all modules by matching signal name (stripping _i / _o
suffixes and matching parameters by name). Rule: only real signal flow is wired
(output drives input; inout / unknown connect freely). All modules are treated as peers —
there is no special TOP node.
No TOP designation needed — every canvas node is just an instantiated submodule. Any port
you leave unconnected automatically becomes a top-level port of the wrapper; ports that
share the same name and direction (e.g. clk on every submodule) are merged into a single
top-level port, so clocks/resets surface as one clk/rst_n driving all instances.
Spec-compliant RTL — generated code follows a standard RTL coding style: standardized file
header, `default_nettype none / `default_nettype wire, 2-space indentation, grouped
input wire / output wire port declarations, aligned internal wire declarations, and
u_<module> instantiations with aligned port maps and // input / // output comments.
Internal connection wires are named from the port base without _i / _o suffixes.
Generate — emits <wrapperName>.v into the workspace root and opens it. Every submodule is
instantiated with all of its ports; connected ports share an internal wire, unconnected
ports become the wrapper's top-level ports.
Create Testbench… — Command Palette → Verilog RTL Connector: Create Testbench…, or
right-click a .v / .sv file in the Explorer (also in the editor context menu). Generates
<module>_tb.v next to the module: the DUT is instantiated as u_<module> with named connections
and every interface signal is declared reg with exactly the port name; clock generation,
reset release (polarity taken from the name), a stimulus skeleton with TODO markers, parameters
mirrored as localparams, a watchdog, an `ifdef DUMP_WAVE VCD dump and the spec file header
are all included. An existing .v file is never overwritten silently (Overwrite / Save As…).
Insert Comment Banner… — Command Palette → Verilog RTL Connector: Insert Comment Banner…,
or the editor right-click menu of a .v / .sv file. Inserts the project's section banner
above the current line, with the current line's indentation:
//==========================================================================
// xxxxxxxxxx
//==========================================================================
The picker offers the banner titles already used in the file (first entry: Custom title…),
so a file keeps a consistent set of section names. The // + 74 = format is identical to the
banners in the RTL spec and in the generated code.
Create Spec-Compliant Wrapper (Ctrl+Shift+P → Create Spec-Compliant Wrapper…) — finds
modules whose ports break the interface rules of the project RTL spec (missing _i / _o /
_b suffix, ascending bit ranges such as [0:7]) and generates a wrapper that exposes a
compliant pin list while instantiating the original module 1:1 inside. The wrapper gets the
standard file header, `default_nettype guards, the source's own //$port_g interface
groups, a 44-column aligned pin list (or keyword groups when the source has no markers),
a u_<module> instantiation with // input / // output comments, and keeps port-level
`ifdef blocks. Clock / reset / interrupt / bus ports are exempt from the suffix rule.
Create Filelist (.f) — two ways in, one command:
- right-click a folder in the Explorer → Verilog RTL Connector: Create Filelist (.f).
Writes
<folder>.f (next to the folder) with the absolute path of every .v / .sv
below it (recursive, sorted, quoted when they contain spaces) and one
+incdir+<absolute directory> line per directory holding a .vh (deduplicated, listed
first). Hidden folders and node_modules are skipped.
Ctrl+Shift+P → Create Filelist (.f) → pick a folder in the dialog. Nothing is
written to disk: the same filelist content is inserted at the cursor of the active editor
(handy to edit by hand or to paste into an existing .f). The block uses the editor's line
endings and only adds a line break where the cursor line really holds code — on an empty line
you get exactly the paths, with no leading/trailing blank line. Ctrl+Z undoes it.
Create Simulation Environment… — Ctrl+Shift+P → Verilog RTL Connector: Create Simulation
Environment…, or right-click a .v / .sv file in the Explorer (also in the editor context
menu, next to Create Testbench…): the right-click route uses the file you clicked instead of
the active editor (with the in-editor content when it is the same file). With the module's file
known (pick the module when the file has several), choose a folder and the extension creates
sim_<module>/ inside it, filled with the bundled VCS + Verdi + SpyGlass simulation
template (sim_test/): compile.sh,
simulate.sh, coverage.sh, cov_analyze.py, run_verdi.sh, sim_lib.sh, lint_extra.tcl,
sim.cfg, both example lists and the template READMEs — 12 files, all copied byte for byte
except the three that depend on the design:
sim.cfg → TOP / TB_TOP (rest untouched),
filelist.f → the module's own source, relative to the environment folder
(../rtl/<module>.v, forward slashes, absolute when it lives on another drive),
tb_filelist.f → the testbenches next to the module (tb_*.v|sv, *_tb.v|sv; its own
<module>_tb.v first), or a TODO comment when there is none.
Paths are never quoted (the template's list parser matches whole lines), so a path with a space
gets a WARN comment instead; the generated lists always use LF. The environment is created
next to the module by default, an existing sim_<module>/ asks before overwriting (files you
added there survive) and sim.cfg is opened afterwards — the notification reminds you to
chmod +x *.sh once on Linux and to review the template's remaining example values
(TB_DEFINES / MACRO_ALIASES / COV_TREES).
Wire Modules… (Auto-Wire) — from the Command Palette: pick the driver and the
receiver module with searchable, mouse-wheel-scrollable drop-downs, then add one row per
signal (name + width + optional driver/receiver port). All the controls of a row sit on one
line with a uniform height; there is no Chinese label anywhere in the panel.
- Show Hierarchy draws the path
driver … common parent … receiver as an SVG (the signal
turns around at the common parent; instance names and the wire label are shown);
- Preview Code lists every file/module that will change, with the exact lines and
// auto-wire: <start> -> <end> : <sig> comments;
- Generate Code inserts the internal
wire, the missing ports (auto-created as xxx_o /
xxx_i when no port matches), the .port (signal) connections and the pass-through
assigns directly into the original files (undoable with Ctrl+Z).
Verilog / SystemVerilog syntax highlighting — the extension ships its own TextMate grammars
(.v → source.verilog, .sv → source.systemverilog) plus a language configuration, so both
languages are tokenised even without any other Verilog extension. Run
Verilog RTL Connector: Apply Vivid Verilog Colors (offered once on first activation) to paint
keywords, types, directions, numbers, strings, comments, $tasks, macros and module/function
names with a vivid, easily distinguishable palette — written into
editor.tokenColorCustomizations for your current theme only (your own rules are kept;
… Remove Vivid Verilog Colors reverts it). Dark and light variants are chosen automatically.
Verilog completion (templates + name hints) — no command needed, it works while you type in
.v / .sv files (the extension activates via onLanguage:verilog / workspaceContains:**/*.v):
type always and get the clocked block in the project format
(always @(posedge clk or negedge rst_n) → if (!rst_n) reset branch → else main branch, §6.1);
type case, function or task for their templates, or type fsm for the
three-stage state machine (state register / next-state logic / output logic, §7.1 — localparam
ST_* states, <fsm>_cs / <fsm>_ns, a default assignment plus a default: branch so it is
latch-free). The FSM template is already recognised by Generate FSM Diagram…. Every template
follows xxx项目_数字设计RTL规范.md, is inserted at the cursor with tab stops and is unit-tested to
be complete (end / endcase / endfunction / endtask always present, block keywords
balanced).
On top of that, names
already written above the cursor (ports, wires, regs, parameters, modules, instances) are
offered as completions, and similar but different spellings are flagged with the reason
(prefix, the xxx_o ↔ xxx_i direction pair §10.3, letter case, or 1–2 characters off).
Toggle it with verilog-rtl-connector.completion.enable / .templates / .names.
Generate Ports from Table… (Port Table) — Ctrl+Shift+P → Generate Ports from Table….
Type the module name and one row per port (Port name, Direction input/output/inout,
Width as the number of bits — 8 becomes [7:0], 1 / empty is a single bit — and an
optional Comment) and use the three panel buttons:
- Check — reports every problem with a rule number, the row and a suggested value: Verilog
itself (
V01 illegal identifier, V02 reserved keyword, V03 duplicate port, V04 width
syntax, V05 module name, V06 port named like the module, V07 case-only duplicate, V08
empty table, V09 comment with a line break) plus the project RTL spec (missing _i / _o /
_b suffix §10.1, ascending range §3.2, 31-character names §11.1, clock/reset first §3.1).
One click on Apply fixes the field;
- Suggest Names — a compliant name per row from
xxx项目_数字设计RTL规范.md §10 / §11
(clock→clk, err→error, address→addr, enable→en, camelCase→lower_case, the
direction suffix with the §10.2 exemptions…), each with its reason and an Apply / Apply all
button;
- Generate Code — the spec-style port list (grouped by interface with
//=== banners,
aligned columns, comments) inserted at the cursor of the active .v / .sv editor;
with no editor available it creates <module>.v in the workspace root with the full spec
skeleton (§1 header + §2 `default_nettype guards). Nothing is generated while Check
still reports Verilog errors.
Check RTL Code… (Lint) — static review of a .v file with fix suggestions (.sv files
are not checked). Open it from the Command Palette (Shift+Ctrl+P, then pick a .v file — the
file of the current editor comes first) or by right-clicking a .v file in the Explorer.
It reports, with line numbers and a suggested fix for every item:
- syntax errors (unbalanced
()/[], module/endmodule, unbalanced begin/end,
case/endcase, function/task/generate/fork pairing — reported per broken block with
the exact line range L12–L40 (where the opener sits and where the closer has to be added),
plus a stray closer as its own single line,
unterminated comments and `ifdef,
missing ;, duplicate modules/ports/signals, case without default, if without
parentheses, = in an if condition, assign with <=, unterminated strings, module header
without ;, nested modules, else without if, comma errors / a port connected twice in a
port map, a case item without :, duplicate default:, a signal that is used but never
declared);
- port-map checks against the workspace (the module index is built once and cached): an
instantiated module that cannot be found, a port that does not exist in that module, a width
mismatch on a connection and ports that are left unconnected;
- not-synthesizable code (
initial, # delays — only real ones: #10, #(1), assign #2;
the #(...) parameter list of module m #(...) and of an instantiation is not reported,
$display/$finish, fork/join,
force/release, wait/while, disable, x/z, casex, clock without edge, always
without an event control, <= in combinational logic, = in clocked logic, if without
else (latch), mixed =/<=, assignment to an input port or to a wire in a procedural
block, a signal driven from two blocks, real/time);
- optimisation suggestions from the project RTL spec (
xxx项目_数字设计RTL规范.md) — port
suffixes _i/_o/_b (§10.1), ascending ranges (§3.2), named port connections (§9.1),
internal signals without _i/_o (§10.4), port groups / clock first (§3.1), the file
header (§1) and the `default_nettype guard (§2).
The report opens in a panel beside the editor (click a line number to jump to it, press
Re-check after editing) and every finding is also published to the Problems view.
The top of the report shows N error(s) / N warning(s) / N suggestion(s) and the number of rule
classes; the findings are then split into three sections — Syntax / usage, Not
synthesizable and RTL spec suggestions — and each section title prints its own severity mix,
so the section numbers always add up to the chips above.
Findings of the same rule are grouped into one collapsible block (group header = rule,
count, severity and one generic fix hint; the line numbers of identical findings are listed
side by side, each with the offending code and its specific fix in the tooltip), so a file with
dozens of findings stays short and readable. If one rule has findings of different severities
the header shows mixed and every row is tagged with its own error / warning /
suggestion badge. A finding that spans several lines (a begin that is never closed) is shown
as L12–L40, hovering it explains the range, clicking it selects exactly those lines, and the
Problems view underlines the whole range.
Block keyword pairing highlight — put the cursor on a begin (or end, case,
endcase, function, endfunction, task, endtask, generate, fork, join, …) and the
keyword you are on and the one it pairs with are both boxed in yellow, so the other half of a
long block is visible at a glance (an overview-ruler mark shows where it is when it is off
screen). Nested blocks pair up level by level, clicking the end finds its begin too, and a
keyword without a partner is boxed in orange as a hint. No command needed; switch it off with
verilog-rtl-connector.blockMatch.enable.
Usage
Open your Verilog project as a folder in VS Code.
Open the command palette (Ctrl+Shift+P) → Verilog RTL Connector: Open Top Connector.
In the Modules tree on the left, click a module's + to add it to the canvas.
The Working panel on the right shows what you've added.
Drag a card header to move a module; drag a port dot onto another port to connect.
Or press Auto Connect to match by name (with _i/_o suffix stripping).
Set a Wrapper name and click Generate (ports you leave unwired become top-level I/O).
To make a legacy module comply with the interface spec there are two entry points:
- Command Palette (
Ctrl+Shift+P) → Verilog RTL Connector: Create Spec-Compliant Wrapper… — scans the whole workspace and lists every module whose ports break the spec.
- Right-click a
.v / .sv file — in the Explorer file list or inside the editor —
→ Verilog RTL Connector: Create Spec-Compliant Wrapper… (only that file's modules;
when the file has exactly one module to fix the picker is skipped).
Either way the extension creates <module>_wrapper.v next to the module it wraps and
opens it. That file contains module <module>_wrapper (spec-compliant port list) plus one
instantiation u_<module> of the original module — the wrapped .v file itself is never
modified (if <module>_wrapper.v already exists you are asked to overwrite or save as).
To build a compile filelist: right-click a folder in the Explorer →
Verilog RTL Connector: Create Filelist (.f). <folder>.f is created next to the folder
with the absolute path of every .v / .sv inside it plus +incdir+ entries for every
directory that contains .vh files. (From the Command Palette the same command asks you for a
folder and inserts the content at the cursor instead of creating a file — use that when you
want to assemble or edit a .f by hand.)
To start simulating a module: open its .v / .sv file (or right-click it in the Explorer —
the file you click wins), then Ctrl+Shift+P →
Verilog RTL Connector: Create Simulation Environment… → pick the folder that should
contain the environment. sim_<module>/ is created there with the bundled VCS / Verdi /
SpyGlass template (compile, simulate, coverage, waveform and lint scripts) and the three
design-dependent files (sim.cfg, filelist.f, tb_filelist.f) are pointed at the module you
ran it on. sim.cfg opens right after; on Linux run chmod +x *.sh once inside the new folder.
To wire two modules together: Ctrl+Shift+P →
Verilog RTL Connector: Wire Modules… (Auto-Wire) → choose the Driver module and the
Receiver module (type to search, or scroll the list with the mouse wheel) → add a row per
signal with its Signal name, Width (8, [7:0], W-1:0, empty = 1 bit) and the
Driver port / Receiver port ((auto match) by default, (auto create new port) to
force a new port, or pick any existing port). Then:
- Show Hierarchy — visualize the hierarchy path and the connection from start to end
(the common parent is the turning point; every edge shows its instance name and its port
mapping);
- Preview Code — see the files/modules/comments that will be modified;
- Generate Code — modify the original files: the connection is created and marked as
added by the auto-wiring tool. New ports are inserted at the head of the port list, and
a level that only receives (or only drives) the signal is connected port-to-port (no
extra internal wire /
assign); the internal wire is only created at the common parent.
Running it twice changes nothing (already wired = skipped).
To create a new module's port list: Ctrl+Shift+P →
Verilog RTL Connector: Generate Ports from Table… → fill the module name and one row per
port (name / direction / width in bits, e.g. 8 / optional comment) → Check to validate the naming (Verilog
- the project spec, with one-click fixes) → Suggest Names for spec-conform names →
Generate Code, which inserts the grouped port list at the cursor of the active
.v / .sv
editor (or creates <module>.v with a full spec skeleton when no editor is open).
Example
Given the sample in sample/example.v (u_src → u_proc, plus top_iface), with only
u_src.data → u_proc.data and u_src.valid → u_proc.valid connected (everything else left
unconnected), Generate produces a spec-styled wrapper:
`default_nettype none
module top_wrapper (
//======================================================================
// Clock and Reset
//======================================================================
input wire clk,
input wire rst_n,
//======================================================================
// External Interfaces
//======================================================================
output wire done,
output wire [7:0] result
);
//==========================================================================
// Internal Wires declarations
//==========================================================================
wire [7:0] data;
wire valid;
//==========================================================================
// Sub-Module Instantiation
//==========================================================================
// Module : top_iface
// Instance : u_top_iface
u_top_iface u_top_iface (
.clk (clk ), // input
.rst_n (rst_n ), // input
.result (result), // output
.done (done ) // output
);
// Module : u_src
// Instance : u_src
u_src u_src (
.clk (clk ), // input
.rst_n (rst_n), // input
.data (data ), // output
.valid (valid) // output
);
// Module : u_proc
// Instance : u_proc
u_proc u_proc (
.clk (clk ), // input
.rst_n (rst_n ), // input
.data (data ), // input
.valid (valid ), // input
.result (result), // output
.done (done ) // output
);
endmodule
`default_nettype wire
Develop
npm install
npm run compile # tsc -> out/
# Press F5 in VS Code with this folder open to launch the Extension Development Host.
Run the logic tests (parser + code generator) without VS Code:
npm run compile
node test/run-tests.js
Package (build a local .vsix)
npm install -g @vscode/vsce # one-time
vsce package --allow-missing-repository
# On Windows PowerShell the .ps1 launcher may be blocked; use the .cmd:
vsce.cmd package --allow-missing-repository
This runs vscode:prepublish (tsc) then produces verilog-rtl-connector-<version>.vsix.
test/, sample/ and the source artwork are excluded via .vscodeignore. Install it in VS Code
via Extensions → ⋯ → Install from VSIX….
Limitations (intentional MVP scope)
- Port parsing targets the common ANSI style (
module m (input wire clk, ...)). Non-ANSI
separated declarations are partially supported; very unusual port constructs may need manual wiring.
- The RTL Code Check is a static, heuristic checker (no compiler / no full parser): it catches
the common mistakes listed above, it is not a replacement for a real simulator or a linter such as
Verilator. It checks
.v files only. There is no syntax highlighting of our own — pair this with
mshr-h.veriloghdl or AndrewNolte.vscode-system-verilog for that.
- Auto Connect only wires real signal flow (
output → input); it does not auto-drive a net
from one input to another. That is intentional — input↔input pairs are simply left
unconnected and therefore surface as top-level wrapper ports (e.g. a shared clk becomes one
top-level clk driving every instance).
License
MIT — do whatever you like, just keep the header.
| |