Skip to content

feat(verilog): index Verilog/SystemVerilog, resolve port bindings, signal access and build profiles - #1875

Open
danusha2345 wants to merge 8 commits into
colbymchenry:mainfrom
danusha2345:feat/hdl-verilog-semantics
Open

danusha2345 wants to merge 8 commits into
colbymchenry:mainfrom
danusha2345:feat/hdl-verilog-semantics

Conversation

@danusha2345

Copy link
Copy Markdown
Contributor

What

Verilog / SystemVerilog support, carved out of the superset draft #1845 onto current main (58c07e8). Five commits, each compiling on its own:

  1. Index Verilog/SystemVerilog — modules, packages, interfaces and modports, named instances, ports and signals, always/assign blocks, functions/tasks and generate scopes; tree-sitter-systemverilog 0.4.0 bundled as wasm. codegraph_explore follows instantiates edges for Verilog, so explore "top uart_rx" prints the path top → uart_bridge → uart_rx. Based on the extractor in feat(verilog): index Verilog/SystemVerilog with module-instantiation edges #402 by @FHYQ-Dong (co-authored).
  2. Resolve instances and port bindings — named, positional and .* connections link an instance's local signals to the instantiated module's formal ports; bit/part selects keep their base signal; local signal references stay in their block scope, and a sync that changes a module header re-derives its whole binding fan-out.
  3. Signal access rolescodegraph_explore with hdlAccess (read / write / readwrite / control / event / all; CLI explore --hdl-access) lists where a signal is read, written, used as a condition or as a clock/reset event, including the direction of arguments passed to known functions and tasks.
  4. HDL build profiles — an optional hdl section in codegraph.json selects source files or filelists, include directories and defines; conditional-compilation branches are indexed under the active profile, status reports configured vs indexed profile, and the watcher follows filelists and included headers.
  5. Computed parameters and widths on requestcodegraph hdl-semantic runs an installed slang (or pyslang via --python) on the active profile and returns evaluated parameters, port widths and macro origins as a separate answer; it never touches the indexed graph and is never run implicitly.

EXTRACTION_VERSION 26 → 27: projects with Verilog files need a re-index. No kernel changes (Verilog is not kernel-routed); other languages' node ids are unchanged.

Verification

  • tsc clean; HDL suites (verilog-*, hdl-*, plus extraction, watcher, mcp-unindexed, sync-rebuild-convergence): 35 files / 287 tests pass. Full suite on Linux: nothing fails here that passes on main (the only differences were dist/viewer-less UI tests on both sides).
  • Real corpora: a Tang Nano 9k card reader (170 files, 33 .v) and a 2-UART bridge; instantiation paths and hdlAccess write sites verified by hand. A TypeScript-only project shows no HDL output. hdl-semantic was exercised earlier on the pulp-platform AXI wrapper with slang / pyslang (parameter overrides 2/4, macro-derived widths 35/36/34); the machine this PR was cut on has neither installed, so the CLI's refusal path is what ran here.

Out of scope

Everything else on #1845 (call evidence, index freshness, MCP lifecycle, C/C++ macros) stays there or is superseded on main; that draft's description lists where each piece went.

danusha2345 and others added 5 commits September 15, 2026 15:47
…and signals

Add a tree-sitter-systemverilog grammar (.v, .vh, .sv, .svh) and an
extractor that records modules, packages, interfaces and modports as
containers, named instances with an `instantiates` reference to the
instantiated module, ports, nets and variables, parameters and `define
macros, functions and tasks, `include imports, always/assign blocks and
lexical/generate scopes. Signal references keep their enclosing block;
bit and part selects keep their base signal. Same-line declarations get
distinct node ids through the column.

The extraction stamp is bumped so existing indexes ask for a re-index.

Based on the extractor contributed in colbymchenry#402.

Co-authored-by: FHYQ-Dong <FHYQ-Dong@users.noreply.github.com>
…erences

Instantiations prefer a synthesizable module over a same-named simulation
stub. Named, positional and wildcard (.*) port connections resolve to the
formal port of the instantiated module and to the local signal; positional
bindings follow the ANSI/non-ANSI header order, wildcard bindings require
known formals and an unambiguous local signal, and explicit connections
win. Member references are matched inside their own scope, package-qualified
calls keep the package identity, and named-symbol flow walks `instantiates`
edges so `codegraph_explore` shows the hierarchy path between two modules.

Sync re-opens a wildcard or positional binding's whole fan-out when a module
header changes, and replays HDL references by their qualified name instead
of re-attaching by short name.
Every signal reference is tagged read, write, readwrite, control (an if /
case / ternary condition) or event (an always sensitivity), and arguments
passed to a known function or task take the direction of that formal.
`codegraph_explore` accepts `hdlAccess` (CLI: `--hdl-access`) with one
exact or qualified signal name and lists the matching access sites with
their source; the wiring of the option is in the following commit.
…her scope

An optional `hdl` section in codegraph.json names profiles of source
files or filelists (+incdir+, +define+, nested -f/-F), include directories,
defines, top modules and language mode. The active profile selects the
HDL units that are indexed and masks the conditional-compilation branches
it disables while preserving offsets; includes contribute macro state and
are tracked as dependencies. The index records the profile fingerprint and
diagnostics, `codegraph status` and `codegraph_explore` report the
configured versus the indexed profile, a context change forces a full HDL
re-index even on a scoped sync, and the file watcher follows filelists,
included headers and explicitly selected ignored paths.

A scope change that lands while a scoped sync is in flight is now
rescheduled: it adds no pending file, only the full-reconcile flag, and the
post-sync check used to drop it.
`codegraph hdl-semantic [query]` (and `CodeGraph.getHdlSemantics()`) runs
an installed slang executable — or, with `--python`, a Python with pinned
pyslang — on a snapshot of the active HDL profile and returns evaluated
parameters, port widths and, for the pyslang backend, the macro origins of
declarations, initializers and types. Facts carry full provenance
(frontend version and hashes, profile and configuration fingerprints, top,
overrides, compiler limits) and link to a source node only when the
indexed file still matches. Nothing runs automatically and a compiler
failure never touches the source graph.

The MCP server instructions gain a Verilog/SystemVerilog section covering
the hierarchy, port-connection, hdlAccess, profile and semantic queries.
…ntics

# Conflicts:
#	src/extraction/index.ts
#	src/index.ts
@danusha2345

Copy link
Copy Markdown
Contributor Author

Merged main (51116a2) after #1878 landed. The two overlaps were in getChangedFiles: the HDL profile filter now sits inside the single candidate loop #1878 introduced (profile sources join the candidate set, are classified whatever their size, and a non-profile Verilog file is skipped), and the sync tail runs commitHdlProfile before finishGitIndexState stamps the commit. tsc clean; HDL, sync, git-index-currency and watcher suites pass. Note: extraction.test.ts currently ends with a Worker exited unexpectedly from tinypool on this machine — it reproduces on a clean main checkout at 51116a2 too (654/655), so it is not from this branch.

…ntics

# Conflicts:
#	src/resolution/name-matcher.ts
@danusha2345

Copy link
Copy Markdown
Contributor Author

Merged main (d0996a2, after #1885): the only overlap was the js-builtins import line in name-matcher.ts. tsc clean; HDL, awaited-receiver and resolution suites pass.

…puted from

`handleParam` only created the constant node, so `localparam K = (N + 1) * 2;`
had no edge to `N` and impact on a width parameter stopped at the parameter
itself. The value expression now goes through the same scoped signal walk the
processes use: identifiers become `hdl:signal:` references FROM the
parameter, callee names and package qualifiers are left out, and the resolver
binds them in the parameter's lexical scope only, so a same-named parameter
of another module is never the target.

Idea and shape by FHYQ-Dong (comment on colbymchenry#402). Measured on two real
designs, nodes unchanged: a Tang Nano 9k card reader +16 edges, pulp-platform
AXI +222 edges; every sampled edge stays inside its module.

Co-authored-by: FHYQ-Dong <FHYQ-Dong@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@danusha2345

Copy link
Copy Markdown
Contributor Author

Added 8f9d7dd at @FHYQ-Dong's suggestion (#402, now closed in favour of this PR): a parameter references the parameters its value is computed from, so impact on a width/depth parameter follows the derived localparam chain inside its module. It reuses the existing scoped signal walk — no new resolver path, no new nodes. Two real designs: +16 and +222 edges, node counts unchanged; Verilog, HDL and extraction suites pass (34 files / 898 tests).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant