Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>Verilog RTL ConnectorNew to Visual Studio Code? Get it now.
Verilog RTL Connector

Verilog RTL Connector

frankxie

|
38 installs
| (2) | Free
Visual drag-and-drop Verilog/SystemVerilog module interconnection with auto wire-matching and top-level wrapper code generation.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

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

  • Editor-title menu (every feature in three categories) — a circuit-board chip icon sits in the editor's top-right action bar, right next to Split Editor Right; clicking it opens a drop-down with three submenus: RTL Design (canvas/connect, instantiate, auto-wire, wrapper, port table, OMIT, comment banner, code templates, FSM diagram, signal path, hierarchy, lint), Simulation (testbench, simulation environment, filelist) and General (vivid colours). It is a native submenu (contributes.submenus + menus["editor/title"]), shown in every editor — each command validates the current file itself.

  • Workspace scan — parses every *.v / *.sv file and extracts module name, parameters, port names, directions (input/output/inout) and bus widths ([7:0]).

  • RTL MODULE HIERARCHY view — under the Explorer it shows the instantiation hierarchy of the active file (u_a (module a) → u_core (module core)); click a node to jump to the instantiation line, (?) marks a module that is not defined in the workspace. Only real instantiations count: a <module> [#(params)] <instance> ( whose instance name sits right next to the module name (spaces / newlines allowed). Compiler directives and macro names never become modules — an `ifdef RW_WLAN_COEX_EN followed by a ( in a continued condition, a `define body, text inside a string ($display("… expected to FAIL (…)")) and a call right after fork are all ignored. Instantiations written inside `ifdef / `else are still found.

  • 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.

  • Import Top Module — the button left of Fit on the zoom bar runs the whole thing backwards: pick a .v / .sv file and its top module is drawn on the canvas — one card for the top itself (highlighted, TOP badge), one card per instantiated sub-module, and a wire for every net connecting them. The top is the module no other module in the file instantiates (if a file holds several unrelated modules you are asked which one to import); sub-module ports come from the workspace scan, assign a = b; counts as one net, and directions that are not declared are deduced from the wiring. Connections are drawn driver → load, so a top-level input drives sub-module inputs and a sub-module output drives the top-level outputs.

    • It round-trips: the TOP card is not instantiated when you press Generate — its ports (names, directions, widths) become the generated module's own ports, so import → tweak the wiring → Generate re-generates that very module instead of wrapping it again. With no TOP card the generator behaves exactly as before.
    • Only one level of hierarchy is expanded, constant / concatenation connections cannot be drawn (those ports stay unconnected), and the top's own #(...) parameters are not rebuilt — every such note is logged to the Output channel as import top note:. Importing replaces the current diagram, so it asks for confirmation first when the canvas is not empty.
  • 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 — asks where to save first (Save As dialog, default <workspace>/<wrapperName>.v) and only writes the file once you confirm — cancel and nothing is written; the file is then opened, and your own edits are yours to save with Ctrl+S. Every submodule is instantiated with all of its ports; connected ports share an internal wire, unconnected ports become the wrapper's top-level ports.

    • Sub-module parameters become localparams: <module abbreviation>_<PARAM> (rw_ble_event_cntl.DATA_WIDTH → BEC_DATA_WIDTH) — the abbreviation drops vendor prefixes (rw / act / hdt / ip) and takes the initials of the remaining words (fifo_sync → FS, ahb2em → AHB2EM); one localparam per module (shared by all of its instances) and the instantiations reference it (#(.DATA_WIDTH (BEC_DATA_WIDTH))).
    • A module placed several times gets numbered nets: instances u_<module>, u_<module>_1, … and the wires they drive carry the same suffix — data, data_1, data_2 — so the two instances can never be shorted onto one net by name.
    • The canvas shows these rules (plus the auto-connect _i / _o matching rule and examples) in red text right above the canvas.
  • 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). You pick the folder (nothing is written before you confirm) and the extension writes a six-file split testbench there — in a tb_<module>/ subfolder it creates inside it (picking that subfolder itself does not add another level), all SystemVerilog:

    File Role
    <module>_package.sv a simulation package <module>_pkg: shared constants, clog2 / to_bin / to_hex / time_tag functions and print_rule / print_info / print_warn tasks (no class is generated — add your own stimulus class there if you want one) — included at file scope and imported by the top
    <module>_init.sv interface signals (all reg, names = port names), testbench parameters (TB_CLK_PERIOD / TB_RESET_CYCLES / TB_TIMEOUT_TIME / TB_SEED), clock generation, reset sequence + default drive, the VCD / FSDB dump selected by a compile-time macro, the absolute-time safety timeout and the tb_check / tb_fail helpers that drive chk_count / err_count
    <module>_inst.sv the DUT instantiation (u_<module>, named connections, #(.P (P)) when the module has parameters)
    <module>_bus_connect.sv commented templates for tie-offs, force / release pokes, pullup and internal-node monitors
    <module>_test_case.sv one task per test case plus the run_test() dispatcher that TB_TEST_CASE selects
    <module>_tb.sv the top / test-framework shell: module <module>_tb; includes the package before the module and the four parts inside it, prints the banner, calls run_test() and prints the [RESULT] ALL TESTS PASSED! / [RESULT] TEST FAILED with %0d errors. verdict

    Because the `include directives of the four parts sit inside the module, they share the signals declared in *_init.sv; the package is file scope and is pulled in with import <module>_pkg::*;. Dumping is opt-in: +define+DUMP_WAVE_VCD writes <module>_tb.vcd ($dumpvars), +define+DUMP_WAVE_FSDB writes <module>_tb.fsdb ($fsdbDumpfile / $fsdbDumpvars / $fsdbDumpMDA, needs the Verdi FSDB runtime), +define+DUMP_WAVE is still accepted as an alias of the VCD one, and without any define nothing is dumped. Only the top has `timescale / `default_nettype / endmodule, and every file carries the spec file header. A clockless module falls back to #(TB_CLK_PERIOD) delays (no @(posedge …) anywhere), and an existing file is never overwritten silently.

  • 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… (<folder>_filelist.f) — two ways in, one command, with different output:

    • right-click a folder in the Explorer (or a .v / .sv / .vh / .svh, which uses its directory) → Verilog RTL Connector: Create Filelist… → the folder is scanned and a save dialog opens, defaulting to <folder>_filelist.f (next to the folder, or inside it when its parent is outside the workspace). The file holds the absolute path of every .v / .sv below the folder (recursive, sorted, quoted when they contain spaces) and one +incdir+<absolute directory> line per directory holding a .vh / .svh (deduplicated, listed first). Header files are never listed as file entries — they are only `included. Hidden folders and node_modules are skipped; cancelling writes nothing.
    • Ctrl+Shift+P → Create Filelist… → pick the folder you want the filelist for. Nothing is written to disk: the same 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. 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),
    • rtl_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) including the ones in its one-level tb_* subfolders — where Create Testbench… puts them; 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).

  • OMIT Module… — Ctrl+Shift+P → Verilog RTL Connector: OMIT Module…, or right-click a .v / .sv file (Explorer and editor context menus; the right-click route uses the file you clicked). The module's main logic is wrapped unchanged in `ifndef <MODULE>_OMIT (the module name upper-cased: backoff.v → `ifndef BACKOFF_OMIT) and the `else branch ties the outputs off, so +define+BACKOFF_OMIT turns the block into an empty shell:

    • wire outputs → assign; reg outputs → one always @(*) begin … end with blocking assignments (no latch, no N09/N14 finding);
    • a 0 tie-off is written as 'd0 — no width, it zero-extends on its own (assign data_o = 'd0;);
    • ready-like outputs are tied to 1 (ready / rdy as its own word, plus the bus channels hready, hreadyout, awready, wready, arready, rready, bready, pready) so an upstream handshake never hangs on the omitted block;
    • inout ports are tied off like outputs (assign sda_b = 'd0;) — a bidirectional port may be read too, so it gets a defined value; inputs are never driven (driving an input from inside the module is illegal/flagged);
    • ports declared inside `ifdef / `ifndef / `elsif / `else in the port list keep their guard: the tie-off is wrapped in the same directives (nesting, elsif and else branches included), so when the macro is off the generated code never references a port that does not exist. Macro widths ([`AW-1:0]) are parsed as widths, not as directives;
    • names are aligned like the spec's §8.2 `else pattern and the inserted lines use the editor's own line endings.

    The newly inserted lines are highlighted (`ifndef plus the whole `else … `endif block) with a soft yellow background, and the highlight disappears as soon as you save. Running it again on a module that is already OMIT-ed is safe: the command compares the guard's `else branch with the current port list and

    • if every output / inout is already tied off, it just says so (“already OMIT-ed and every output / inout port is tied off — nothing new to OMIT”) and changes nothing;
    • if the port list grew (a new output / inout was added later), it only appends the missing tie-offs before `endif — the existing ones are never duplicated. Non-ANSI port lists (directions declared in the body) are refused. The edit is applied to the editor buffer only (one Ctrl+Z undoes it) and you are asked Save / Keep Unsaved — if you keep it unsaved the file stays modified and the dot next to its name in the Explorer marks it, ready for Ctrl+S.
  • Remove OMIT Module… — the undo of the above: `ifndef <MODULE>_OMIT and the whole `else … `endif tie-off branch are deleted, so the module body is back to its original self (byte-for-byte — blank lines around the body included). Use it when the block no longer needs to be stubbed out: Ctrl+Shift+P → Verilog RTL Connector: Remove OMIT Module…, or right-click the .v / .sv file (in a multi-module file only the modules that really carry the guard are offered). A file without the guard is reported (“not OMIT-ed”) and left untouched; the same Save / Keep Unsaved prompt follows, and the yellow highlight is dropped.

  • Every edit the extension makes is highlighted — whenever a command writes into a document (OMIT / Remove OMIT, Auto Instantiate, Insert Comment Banner, Insert Code Template, Create Filelist at the cursor, Wire Modules), the lines it actually changed get a soft yellow background plus a mark on the overview ruler. The highlight stays until you save the file — scrolling, switching files, undoing or waiting do not clear it, and files that were not visible at edit time get their highlight when you open them. Newly created files (wrapper, testbench, sim env, port-table skeleton, exported diagrams) are not highlighted.

  • Show Schematic Diagram — select a piece of Verilog (an assign, one always block, a case, an instantiation — anything from one line to a whole module) and right-click → Verilog RTL Connector: Show Schematic Diagram. The selection is checked first: an unbalanced begin … end, case … endcase, module … endmodule, an unclosed (, a dangling `ifdef or a selection without any drawable statement is reported (naming exactly what is missing) and nothing is drawn; a complete selection opens a circuit schematic beside the editor, drawn in the hand-drawn textbook style: data flows left → right, signal names are plain monospace text (inputs on the left, the output name in red bold), and every cell is a big block with a title plus an italic type line — comparators in purple (== IDLE / comparator), MUX in orange as a plain rectangle with 0 / 1 on its inputs and SEL at the bottom (MUX / 2:1), DFFs in teal with D / CLK / CLR_N / Q pins, a filled clock triangle, a low-active bubble and the register name as subtitle, and combinational logic as real gate symbols (AND / OR / NOT / XOR with the classic &, ≥1, =1 glyphs; + - == as rounded blocks). Wires are 2 px with junction dots, routed to cross as little as possible and never cut through a cell (they go along a free row, over the top or around the bottom), net names are printed on them, and the clock / reset nets of a DFF always get port cells so CLK / CLR_N really connect. A green box at the bottom prints the next-state formula of every register (D(q) = en ? d : Q(q)), the title is centred above a thin rule, boxes carry the source line in their tooltip, and Refresh re-reads the current selection. The diagram is drawn with HTML + CSS (divs, border-radius, clip-path), so the text stays crisp at any zoom; Save as SVG / Save as PNG export it as an image. Typical uses: read a mux chain, check what an always block really builds, or paste a schematic into a review.

  • Configure Macro — Ctrl+Shift+P → Verilog RTL Connector: Configure Macro (or the editor-title menu under RTL Design ▸ Check). It scans every macro of the project (.v / .sv / .vh / .svh) and lists them in a panel where:

    • switches (`define X — no value) get a checkbox, value macros (`define X 16) get a checkbox + a value box;
    • commented-out defines (//define RW_DM_XBAR_BUS`) are found too and show up as off, ready to be ticked — that is how RivieraWaves-style projects switch features on and off;
    • master switches are highlighted with ★ and sorted first (a macro referenced by the `ifdef of at least 5 % of the files, minimum 2 files — 17 of 177 in the bundled 251-file test project), with a master switches only filter;
    • every row shows how often it is used in `ifdef / `ifndef / `elsif, in how many files, and a clickable file.v:line of its definition; a filter box, Apply, Reset, Refresh and Copy defines (one `define line per enabled macro → clipboard) sit above the list. Code that is not compiled is greyed out: with the configured set, every line inside an `ifdef / `ifndef branch whose condition is false — and the losing branch of an `elsif / `else chain — is drawn in grey, nesting included, following a file's own `define / `undef in order and updating as you type. It is display only (no file is ever changed) and can be switched off with verilog-rtl-connector.macroHighlight.enable. Your choices are saved to .vscode/verilog-rtl-connector.macros.json, a plain diff-able JSON you can commit.
  • Show Signal Path — put the cursor on a signal (port, wire / reg / logic) and right-click → Verilog RTL Connector: Show Signal Path (or the editor-title menu under RTL Design): a diagram opens beside the editor with the driver module on the left and every load module on the right, arrows always driver → load. Each box is the instance name (u_rw_ble_event_cntl_scanini) over its clk: / rst: lines and the ports that carry the signal (in .gnt_i / out .gnt_o) — one field per line, so text never overlaps (long names are clipped to …, and a long clk: / rst: list shows the first name plus +N with the full list in the tooltip). Boxes are sized from their own content per column (clamped to 150–360 px, side padding 10 px), so every load box is the same size while a short (outside) box does not waste the width of the widest box; the type is large enough for screenshots (instance name 17 px, clk/rst 14 px). The driver and the load modules are looked up in the whole project (every .v / .sv in the workspace, not just the folder of the current file) — a driver in the parent IP or a load in a sibling folder is found instead of showing up as a dashed placeholder, and the header states how many files were scanned. Every box ends with a role line saying what it does with the signal: real load / real driver (it reads / drives it in its own logic) versus path (the signal only passes through it on the way to a child instance), spelled out as real load + path when both apply — so a relay module is never mistaken for a real consumer. Only modules that really use the signal are drawn: a module that merely routes the signal on gets no box at all, and its instance chain is printed inside the real-load box as a small blue-grey path: line — every hop joined with ->, wrapped continuation lines keep the arrow and line up, and a name that cannot fit is clipped to … (the full chain is always in the tooltip): path: u_rw_ble_core -> u_rw_ble_core_cntl -> u_rw_ble_event_cntl -> u_rw_ble_event_cntl_pti (that is et_pti_prio in rw_ble_event_cntl.v; the notes list the modules that were left out). The search follows the hierarchy up to 6 levels (which is what makes a driver three levels above the cursor show up instead of a dashed placeholder) and also follows the signal down through instantiations up to 4 levels, so a real load hidden inside a wrapper is found and reported as real load while the wrapper stays path. The header (and the panel line) names them in words, e.g. real load: u_rw_ble_event_cntl_pti · path only: u_rw_ble_event_cntl, u_rw_ble_core_cntl, u_rw_ble_core. Hover a box to see that module's full .v file path and every clock / reset it has. Colours follow the role, never the other way round: drivers (including a box that both drives and loads) are purple, loads are orange, and the module the cursor is in uses a darker shade of its own role colour and has every line of its box in bold (no current module badge) — so a driver box can never look like a load box. Unresolved modules are dashed grey, and the arrows (including the arrow head) are drawn in the colour of the driver they leave. Every box shows its clock and reset as clk: macCoreClk / rst: macCoreClkHardRst_n — and a clock is only a signal that an always sensitivity list is actually clocked by (@(posedge …)): a port that merely has clk in its name (clkncnt) is not a clock, and neither is an assert property (@(posedge …)) edge. When a module has dozens of @(posedge clk …) plus one stray @(posedge clkncnt), the dominant clock wins; a purely combinational module honestly shows clk: -. Resets are name-checked and used in the body, so master_nrst + master_soft_rst and hreset_n all appear correctly. An output port of an instantiated module drives the net, an input port loads it, assign s = … / s <= … makes the enclosing module itself the driver, and a signal on a right-hand side or in a condition means the module reads it. When the signal is an input / output of the enclosing module the parent instantiation is followed too (2 levels up), so the driver of an input port shows up as the module above it — first searching the current directory, then the whole workspace (cached, ~300 ms for 250 files). Unresolved drivers / loads become dashed (outside) / (no load) boxes with the reason printed under the diagram, and a fan-out cap (16 boxes) with a note keeps a clock net readable. Branches leaving a driver share one trunk and each branch is labelled just above the line with the load port it enters (.cnt_i), so every label sits on its own wire. Refresh re-reads the cursor, Save as SVG / PNG exports the picture.

  • 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) and the .port (signal) connections into the editor buffers of the original files — it does not save them: the modified files are opened for you, you review the changes and save them yourself (Ctrl+K S, or the Save These Files button in the notification); Ctrl+Z undoes everything. The inserted code follows each file's own formatting — indentation (tabs or the file's space step), the columns the neighbouring port entries / .port (net) entries use (the most common column in that file wins), and the // auto-wire: … comment is written in the right margin, aligned with the file's existing trailing comments (port / wire lines get // auto-wire: …, connection lines get // output [auto-wire: …]).
  • Verilog / SystemVerilog syntax highlighting — the extension ships its own TextMate grammars (.v + .vh → source.verilog, .sv + .svh → source.systemverilog) plus a language configuration, so all four extensions are tokenised even without any other Verilog extension (.vh / .svh header files were previously opened as plain text). 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.

  • Header files (.vh / .svh) are first-class citizens — they open as Verilog / SystemVerilog with highlighting, comment toggling, block matching, completion, Ctrl+Click (including `include "defs.vh" → the included file), // TODO highlighting and the OUTLINE view; they are also scanned across the whole project together with the sources, so macros / typedefs / parameters / functions declared in a header take part in the module index, the hierarchy scan, the signal-path diagram and the instantiation port-direction lookup. Because a header is not a compilation unit, Create Filelist… emits a +incdir+ line for its directory instead of a file entry, Check RTL Code… still lints .v only, and module-level commands (wrapper / testbench / simulation env / OMIT) stay on .v / .sv; Insert Code Template… and Insert Comment Banner… are available in headers too. A workspace holding only header files activates the extension (workspaceContains:**/*.vh / *.svh).

  • Verilog completion (templates + name hints) — no command needed, it works while you type in .v / .sv / .vh / .svh files (the extension activates via onLanguage:verilog / workspaceContains:**/*.v / *.vh / *.svh): 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, Type wire (default) or reg, Signed signed (default) or unsigned, Width as the number of bits — 8 becomes [7:0], 1 / empty is a single bit — and an optional Comment) and use the panel buttons:

    • Type is the net / variable keyword of the declaration: leave it on wire (the default) for most ports, pick reg for an output driven from an always block — the generated line becomes output reg [3:0] cnt_o,;
    • Signed is off by default: an unsigned port writes nothing at all, so the keyword only shows up on the rows where you actually pick signed (wire signed / reg signed). The columns stay aligned (unsigned rows keep the spaces so widths line up) and when no row is signed the column disappears from the output completely;
    • 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, V10 unknown type, V11 unknown SIGNED value, V12 bad macro line, V13 unknown directive name, V14 unmatched `ifdef / `endif) 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 — asks where to save first (default <module>.v next to the active editor, or in the workspace root), then writes the full spec skeleton (the port list, aligned columns, comments + §1 file header + §2 `default_nettype guards) and opens it. An existing file is only overwritten after a modal confirmation; cancelling writes nothing. The notification offers Copy Port List for pasting the list into an existing file. Nothing is generated while Check still reports Verilog errors;
    • + Add Port / + Add Comment / + Add Macro — a new row is inserted below the row you last clicked. + Add Macro inserts a conditional-compilation row: type the directive (`ifdef SIMPLE, `ifndef SIM_DBG, `else, `endif // SIMPLE, `define …) and it is printed exactly as typed (indentation included) at that position, so the ports after it are compiled only when the macro matches. Macro rows are violet, Check reports unmatched `ifdef / `endif (V14) and a line without the leading backtick (V12), and a port name that appears in two different branches is not reported as a duplicate. + Add Comment inserts a section banner: you type the title, the tool puts the two //=== rules around it (//===== / // title / //=====), skips it entirely when the title is empty and inserts a blank line before it. The input of that row is green (the same green as the Excel buttons), so a banner row is easy to tell apart from a port row. The Clock and Reset banner is the only one added for you — every other section is yours to place, and the ports otherwise keep exactly the order of the table (only clock / reset ports are moved to the top, and only while the Clock and reset ports first (§3.1) checkbox is ticked);
    • Import From Excel / Export To Excel (green) — round-trip the table through the port_table_template.xlsx layout (信号名 / 位宽 / I/O / 类型 / SIGNED / From / To / 默认值 / 信号描述; a row with only the first column is a section title, a //-prefixed one is a banner, the width column holds the number of bits). Columns are recognised by their header, so an older 7-column sheet (and a sheet whose columns were reordered) still imports — a missing 类型 / SIGNED cell just means wire + unsigned. Import From Excel reads a .xlsx / .xlsm and replaces the table (with a modal confirmation when the table already has rows), mapping I/O/IO to the direction dropdown and flagging unknown I/O and unusable names instead of dropping rows silently. Export To Excel saves the current table in the same layout (default <module>_ports.xlsx) and offers Open in Excel. From / To / default value / group are not shown in the panel — they stay attached to each row (hover a row to see them) and only take part in the Excel round-trip, never in code generation. Both directions are implemented in a hand-written, dependency-free src/xlsx-core.ts (own ZIP writer/reader, own CRC32, zlib.deflateRawSync). The shipped port_table_template.xlsx is a small, ready-to-use example: clk / rst_n, two // section banners and both width spellings (a bit count and the parameter AA_WIDTH=10);
    • New Port Table (pink, top-right corner) — resets the panel so you can start a new table: module name, all rows, the Check/Suggest results and the code preview are cleared, the §3.1 checkbox goes back to its default and the two starter rows (clk, rst_n) are back. No file is touched, and when the table is not empty a modal confirmation says exactly what is about to be cleared.

    Width can be a custom macro. An all-upper-case name becomes a module parameter: WIDTH → [WIDTH-1:0] plus parameter WIDTH = 8, and WIDTH with an explicit default — AAAA=5 — becomes parameter AAAA = 5 (a name used more than once takes the default from the occurrence that has one). Macros inside a range ([DATA_WIDTH-1:0]) are collected as well; the parameter list is de-duplicated in order of appearance with the = signs aligned:

    module rw_ble_core_cntl #(
      parameter DATA_WIDTH = 32,
      parameter CFG_W      = 8
    ) (
      //======================================================================
      // Clock and Reset
      //======================================================================
      input  wire                  [CFG_W-1:0] cfg_i,       // configuration
    
      //======================================================================
      // AXI write channel
      //======================================================================
      input  wire             [DATA_WIDTH-1:0] axi_data_i,  // write data
      input  wire                              valid_i      // write valid
    );
    
  • 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.

  • Go to Definition (Ctrl+Click) — no command, no activation: open a .v / .sv file, hover a name and Ctrl+Click (⌘+Click on macOS) to jump to

    • `include "file.sv" → the included file (resolved relative to the current file, then a workspace-wide search by file name);
    • a task / function call or declaration → the declaration (the return type is skipped, so function automatic int clog2(…) jumps to clog2);
    • a class usage (Foo x = new();, Foo::bar(), function Foo copy();) → class Foo;;
    • a port name, a signal (wire / reg / logic / bit / genvar / integer …, multi-line declarations included), a parameter / localparam (also the ones in the module header's #(...)) → its declaration;
    • an instance name (u_fifo, e.g. in a hierarchical reference u_fifo.busy_o) → the module it instantiates (and an instantiation's module name → that module's declaration);
    • a module / interface / program / package / interface name → its declaration;
    • a macro — `RW_BLE_SUPPORT (also when the cursor sits on the backtick), the bare name in `ifdef / `ifndef / `elsif / `undef, and a value macro used without a backtick in an expression → its `define line (current file → include chain → whole workspace, so macros that only live in a defines.v / xxx.vh are found);
    • several same-named declarations → a peek list with the definition from the cursor's scope first.

    Cross-file works out of the box: the `include chain is followed (up to 5 levels) and the sibling parts of a split testbench (<stem>_package / _init / _inst / _bus_connect / _test_case / _tb.sv) are indexed, so run_test() in xxx_tb.sv jumps into xxx_test_case.sv and print_info() in xxx_test_case.sv jumps into xxx_package.sv. When none of those has the name, the whole workspace is searched (index built on the first such click, reused for 60 s, invalidated on save), so an instantiated module that lives in another file — backoffACSel in backoff.v — jumps to module backoffACSel in backoffACSel.v; definitions from the same directory are preferred. Comments and strings are masked before scanning (a commented-out // task foo(); or a //`define X is never a definition). If nothing happens, run Verilog RTL Connector: Show Go to Definition Status to see the setting, the word under the cursor, the include-chain symbols, the workspace index and the candidates it found. Switch the feature off with verilog-rtl-connector.gotoDefinition.enable.

  • Hover: what is this name? — no command needed. Rest the mouse on a name and a popup shows

    • a signal / port / parameter: the declaration line as written (its trailing comment included), a type summary such as output [7:0], the owning module and file · line;
    • a macro (`NAME or `ifdef NAME): its `define line with the value (`define RW_DM_ADDRESS_WIDTH 16), the comment above the definition and where it lives (defines.v · line 37);
    • an instance name → instance of <module> plus the declaration line, and a module / task / function / class → its declaration.

    It uses exactly the same resolution as Ctrl+Click, so everything you can jump to is also shown, and the definition from another file is read (and cached) on the fly. Hover never triggers a workspace scan of its own — it reuses the index the Ctrl+Click already built, so moving the mouse stays fast. Switch it off with verilog-rtl-connector.hover.enable.

  • Explorer OUTLINE: all tasks and all functions — the OUTLINE view under the Explorer lists the routines of the active file in two groups, Tasks (n) and Functions (n), in source order; the grey text on the right is the owning module / package / class, and clicking an entry selects the whole body (declaration line → endtask / endfunction). It is on by default and needs no command — open a .v / .sv file and it is there (the same symbols also feed Go to Symbol, Ctrl+Shift+O, and the breadcrumbs). Comments and strings are ignored, so a commented-out // task foo(); never appears. Switch it off with verilog-rtl-connector.outline.enable and the OUTLINE goes back to whatever else you have.

  • // TODO highlight — switched on by Apply Vivid Verilog Colors — the shipped TextMate grammar tags TODO / FIXME / XXX inside line and block comments with the scope keyword.other.todo.verilog, but nothing is coloured until you run Verilog RTL Connector: Apply Vivid Verilog Colors. That one command (a) writes the palette — where the TODO row is a bright yellow #FFEB3B bold + underlined text colour for dark themes (#A67C00 for light ones) — into your editor.tokenColorCustomizations, and (b) sets verilog-rtl-connector.todoHighlight.enable to true. Remove Vivid Verilog Colors takes both away again. While it is on, an editor decoration adds the highlighter chip: #FFEB3B background (configurable, opaque by default), #3B2E00 text (verilog-rtl-connector.todoHighlight.foregroundColor, empty = keep the theme colour), a dark amber outline — plus an overview-ruler mark and a status-bar counter ($(checklist) TODO: 3) while the active RTL file has hits — click it (or run Show TODO Highlight Status from the palette) for the setting values, the active editor and the hit lines. Only comments are scanned (a "// TODO" string literal is ignored), matching is case-insensitive and whole-word, and //TODO without a space, tabs and CRLF all work. You can also flip verilog-rtl-connector.todoHighlight.enable yourself, and tune .keywords / .backgroundColor / .foregroundColor.

Usage

  1. Open your Verilog project as a folder in VS Code.

  2. Open the command palette (Ctrl+Shift+P) → Verilog RTL Connector: Open Top Connector.

  3. 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.

  4. 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).

  5. Set a Wrapper name and click Generate (ports you leave unwired become top-level I/O).

  6. 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).

  7. To build a compile filelist: right-click a folder in the Explorer → Verilog RTL Connector: Create Filelist… → choose where to save it (the dialog defaults to <folder>_filelist.f). From the Command Palette the same command asks which folder the filelist is for and then inserts the content at the cursor instead of writing a file — use that when you want to assemble or edit a .f by hand.

  8. 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, rtl_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.

  9. To disable a block for a while: open its file (or right-click it in the Explorer) → Ctrl+Shift+P → Verilog RTL Connector: OMIT Module…. The whole module body is wrapped in `ifndef <MODULE>_OMIT (module name upper-cased) and the `else branch ties the outputs off ('d0, ready-like signals to 1); the inserted lines stay highlighted until you save. You are then asked whether to save. Run it again on the same module and only new output / inout ports get a tie-off (or you are told there is nothing left to do); Verilog RTL Connector: Remove OMIT Module… puts the original body back.

  10. 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).
  1. 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 / type wire or reg / signed on or off / 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 asks for a file name/location and then writes the spec skeleton (port list + §1 header + §2 guards) and opens it. Already have the ports in a spreadsheet? Import From Excel loads a .xlsx written in the port_table_template.xlsx layout, and Export To Excel saves whatever is in the table back to the same layout.

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.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft