diff --git a/analysis_options.yaml b/analysis_options.yaml
index 2b2098177..973381af9 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -9,6 +9,7 @@ analyzer:
exclude:
- doc/tutorials/chapter_9/rohd_vf_example
- rohd_devtools_extension
+ - rohd_extension
# keep up to date, matching https://dart.dev/tools/linter-rules/all
# some lints are not yet available, so disabled and marked with [not currently recognized]
diff --git a/doc/tutorials/chapter_6/answers/exercise_2_n_bit_subtractor.dart b/doc/tutorials/chapter_6/answers/exercise_2_n_bit_subtractor.dart
index 5765f08bf..18efb5a2d 100644
--- a/doc/tutorials/chapter_6/answers/exercise_2_n_bit_subtractor.dart
+++ b/doc/tutorials/chapter_6/answers/exercise_2_n_bit_subtractor.dart
@@ -7,7 +7,6 @@ import '../../chapter_3/answers/helper.dart';
import '../../chapter_5/answers/full_subtractor.dart';
class FullSubtractorComb extends FullSubtractor {
- @override
FullSubtractorComb(super.a, super.b, super.borrowIn) {
// Declare input and output
final a = input('a');
diff --git a/pubspec.yaml b/pubspec.yaml
index 01f1ac72f..0a93f032d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -13,6 +13,7 @@ dependencies:
collection: ^1.15.0
logging: ^1.0.1
meta: ^1.9.0
+ stream_channel: ^2.1.0
test: ^1.17.3
dev_dependencies:
diff --git a/rohd_extension/.markdownlint.json b/rohd_extension/.markdownlint.json
new file mode 100644
index 000000000..fe1bf1caa
--- /dev/null
+++ b/rohd_extension/.markdownlint.json
@@ -0,0 +1,4 @@
+{
+ "MD013": { "tables": false, "code_blocks": false },
+ "MD060": false
+}
diff --git a/rohd_extension/Makefile b/rohd_extension/Makefile
new file mode 100644
index 000000000..911487565
--- /dev/null
+++ b/rohd_extension/Makefile
@@ -0,0 +1,108 @@
+# Makefile for the ROHD VS Code Extension (rohd_extension)
+#
+# Targets:
+# compile - Compile TypeScript sources to out/
+# install - Compile and install to remote VS Code Server (default)
+# install-local - Compile and install to local VS Code
+# install-remote - Compile and install to remote VS Code Server
+# clean - Remove compiled output and packaged .vsix files
+# real-clean - clean + remove node_modules, lock files, .dart_tool
+# help - Show this help
+
+ROOT := $(shell pwd)
+TS_SOURCES := $(shell find $(ROOT)/src -name '*.ts' 2>/dev/null)
+
+# Read name/version from package.json
+PKG_NAME := $(shell node -p "require('./package.json').name")
+PKG_VERSION := $(shell node -p "require('./package.json').version")
+PKG_PUBLISHER := $(shell node -p "require('./package.json').publisher")
+
+# VS Code Server extension directory (remote dev container)
+REMOTE_EXT_DIR := $(HOME)/.vscode-server/extensions/$(PKG_PUBLISHER).$(PKG_NAME)-$(PKG_VERSION)
+# Local VS Code extension directory
+LOCAL_EXT_DIR := $(HOME)/.vscode/extensions/$(PKG_PUBLISHER).$(PKG_NAME)-$(PKG_VERSION)
+
+# Stamp file to track last successful compile
+COMPILE_STAMP := out/.compile-stamp
+
+.PHONY: all help compile install install-local install-remote clean real-clean
+
+all: install
+
+help:
+ @echo "ROHD VS Code Extension - Build Targets"
+ @echo ""
+ @echo " compile - Compile TypeScript to out/"
+ @echo " install - Compile and install to remote server (default)"
+ @echo " install-local - Compile and install to local VS Code"
+ @echo " install-remote - Compile and install to remote VS Code Server"
+ @echo " clean - Remove compiled output and .vsix files"
+ @echo " real-clean - clean + node_modules, lock files, .dart_tool"
+ @echo ""
+ @echo "Extension: $(PKG_PUBLISHER).$(PKG_NAME) v$(PKG_VERSION)"
+ @echo "Remote: $(REMOTE_EXT_DIR)"
+
+# ---------------------------------------------------------------------------
+# Dependencies
+# ---------------------------------------------------------------------------
+
+# Install npm dependencies (including TypeScript) if missing or out of date
+node_modules: package.json
+ @echo "Installing npm dependencies..."
+ @npm install
+ @touch node_modules
+
+# ---------------------------------------------------------------------------
+# Compile
+# ---------------------------------------------------------------------------
+
+$(COMPILE_STAMP): $(TS_SOURCES) tsconfig.json package.json node_modules
+ @echo "Compiling TypeScript..."
+ @npx tsc -p ./
+ @touch $(COMPILE_STAMP)
+
+compile: $(COMPILE_STAMP)
+
+# ---------------------------------------------------------------------------
+# Install
+# ---------------------------------------------------------------------------
+
+install-remote: compile
+ @if [ -d "$(REMOTE_EXT_DIR)" ]; then \
+ echo "Installing to $(REMOTE_EXT_DIR)/out/..."; \
+ cp out/*.js out/*.js.map "$(REMOTE_EXT_DIR)/out/"; \
+ echo "Done. Reload the VS Code window to pick up changes."; \
+ else \
+ echo "ERROR: Extension not found at $(REMOTE_EXT_DIR)"; \
+ echo "Install the extension first via VS Code, then use this target to update."; \
+ exit 1; \
+ fi
+
+install-local: compile
+ @if [ -d "$(LOCAL_EXT_DIR)" ]; then \
+ echo "Installing to $(LOCAL_EXT_DIR)/out/..."; \
+ cp out/*.js out/*.js.map "$(LOCAL_EXT_DIR)/out/"; \
+ echo "Done. Reload the VS Code window to pick up changes."; \
+ else \
+ echo "ERROR: Extension not found at $(LOCAL_EXT_DIR)"; \
+ echo "Install the extension first via VS Code, then use this target to update."; \
+ exit 1; \
+ fi
+
+install: install-remote
+
+# ---------------------------------------------------------------------------
+# Clean
+# ---------------------------------------------------------------------------
+
+clean:
+ @echo "Cleaning compiled output and .vsix files..."
+ @rm -rf out/
+ @rm -f *.vsix
+
+real-clean: clean
+ @echo "Removing node_modules, lock files, and Dart build artifacts..."
+ @rm -rf node_modules/
+ @rm -f package-lock.json
+ @rm -rf dart/.dart_tool/
+ @rm -f dart/pubspec.lock
diff --git a/rohd_extension/README.md b/rohd_extension/README.md
new file mode 100644
index 000000000..f0d1eba28
--- /dev/null
+++ b/rohd_extension/README.md
@@ -0,0 +1,309 @@
+# ROHD VS Code Extension
+
+A VS Code extension for the [ROHD](https://github.com/intel/rohd) hardware
+design framework. It provides context-aware Dart code snippets, cross-probe
+source navigation from ROHD viewers (schematic, waveform), and automatic
+port-forwarded URI display for the Dart Tooling Daemon (DTD) and VM Service.
+
+## Features
+
+- **Context-aware ROHD snippets** — conditional constructs (`If`, `Iff`,
+ `ElseIf`, `Else`, `Case`, `CaseZ`) only appear when the cursor is inside
+ a `Combinational` or `Sequential` block. Top-level patterns (`Module`,
+ `Sequential`, `Combinational`, `FSM`, etc.) are always available.
+- **Cross-probe source navigation** — click a signal or module in an ROHD
+ viewer and jump directly to the corresponding Dart source (FLC — **F**ile,
+ **L**ine, **C**olumn — crossprobing).
+- **Debug adapter tracking** — registers a `DebugAdapterTrackerFactory` for
+ Dart sessions to automatically capture DTD and VM Service URIs with
+ port-forwarding awareness.
+- **DTD bridge** — registers `rohd.goToSource` / `rohd.resolveFrames`
+ services on the Dart Tooling Daemon so the DevTools extension can navigate
+ the editor remotely.
+
+On activation the extension prints:
+
+```text
+════════════════════════════════════════════════════════════
+ROHD 0.3.0: Extension loaded for FLC crossprobing.
+
+DTD:
+ URI: ws://127.0.0.1:44123/token
+ Fwd: ws://localhost:58201/token ← only shown if port differs
+VM:
+ URI: ws://127.0.0.1:40699/TOKEN=/ws
+ Fwd: ws://localhost:61969/TOKEN=/ws ← only shown if port differs
+════════════════════════════════════════════════════════════
+```
+
+## Snippets
+
+### Always available (top-level)
+
+| Prefix | Expands to | Description |
+|--------|-----------|-------------|
+| `mod`, `Module` | `class … extends Module { … }` | Module scaffold with `addInput`/`addOutput` |
+| `seq`, `Sequential` | `Sequential(clk, [ If(reset, …) ])` | `always_ff` block with reset pattern |
+| `comb`, `Combinational` | `Combinational([ … ])` | `always_comb` block |
+| `assign` | `out <= expr;` | Continuous assignment (outside `_Always`) |
+| `sim`, `Simulator` | Clock, reset, `WaveDumper`, `Simulator.run()` | Simulation / testbench boilerplate |
+| `fsm`, `FSM` | `FiniteStateMachine` scaffold | FSM with states, events, actions |
+| `example`, `counter` | Full counter module | ROHD counter reference example |
+| `vf`, `tb`, `testbench` | `rohd_vf` testbench | Agent / Driver / Monitor / Sequencer template |
+
+### Context-aware — file scope
+
+These appear only at file/top level (not inside a function or class body).
+
+| Prefix | Expands to | Description |
+|--------|-----------|-------------|
+| `FSM`, `fsm` | enum + `class extends Module` + `FiniteStateMachine` | Full FSM scaffold at file level |
+| `Module`, `mod` | `class extends Module { addInput/addOutput … }` | Module scaffold |
+| `Interface`, `intf` | enum + `class extends Interface
` + `clone()` | Classic Interface with direction enum, `setPorts`, and `clone()` |
+| `PairInterface`, `pairintf` | `class extends PairInterface { … clone() }` | PairInterface with provider/consumer roles |
+
+### Context-aware — module body scope
+
+These appear when the cursor is inside a `class … extends Module` body.
+
+| Prefix | Expands to | Description |
+|--------|-----------|-------------|
+| `Pipeline`, `pipe` | `Pipeline(clk, stages: [(p) => […]])` | Pipelined datapath |
+| `ReadyValidPipeline`, `rvpipe` | `ReadyValidPipeline(clk, stages: …, valid, ready)` | Pipeline with flow control |
+| `Sequential`, `seq` | `Sequential(clk, [If(reset, …)])` | `always_ff` block |
+| `Combinational`, `comb` | `Combinational([…])` | `always_comb` block |
+
+### Context-aware — inside `Combinational` or `Sequential`
+
+These snippets only appear when the cursor is inside a `Combinational([…])`
+or `Sequential(clk, […])` block, matching ROHD's requirement that
+conditionals live inside an `_Always` block.
+
+| Prefix | Expands to | Description |
+|--------|-----------|-------------|
+| `If` | `If(cond, then: […], orElse: […])` | Inline if/else (most common) |
+| `ifthen` | `If(cond, then: […])` | Simple conditional guard |
+| `ifnested`, `iforelse` | `If(a, then: …, orElse: [If(b, …)])` | Nested if / else-if / else chain |
+| `If.block`, `ifblock` | `If.block([Iff(…), ElseIf(…), Else(…)])` | Flat if/else-if/else block chain |
+| `Iff`, `iff` | `Iff(cond, […])` | First clause in `If.block` (two f's) |
+| `ElseIf`, `elseif` | `ElseIf(cond, […])` | Middle clause in `If.block` |
+| `Else`, `else` | `Else([…])` | Final clause in `If.block` |
+| `Case` | `Case(expr, [CaseItem(…)], …)` | `case` / `unique case` / `priority case` |
+| `CaseZ`, `casez` | `CaseZ(expr, [CaseItem(…)])` | Don't-care matching with `z` syntax |
+| `CaseItem`, `caseitem` | `CaseItem(value, […])` | Single arm inside `Case`/`CaseZ` |
+| `assign` | `out < expr,` | Conditional assignment (inside `_Always`) |
+
+> **Note:** `Iff` (two f's) is *not* `If` — it is the first entry in an
+> `If.block([…])` chain. The double-f distinguishes it from `If(cond,
+> then: …, orElse: …)` which is a self-contained conditional.
+
+## FLC Cross-Probing
+
+FLC (**F**ile, **L**ine, **C**olumn) data maps every signal and submodule
+in the generated output back to the Dart source location where it was
+constructed. The extension uses FLC data to navigate from a schematic or
+waveform viewer directly to the ROHD Dart source.
+
+### FLC JSON Format (v6)
+
+An `.flc.json` file uses a shared ROHD source file table plus a per-module
+trie of source frames. Each trie leaf is a compact symbol string for a
+signal or submodule instance:
+
+```json
+{
+ "version": 6,
+ "files": [
+ "lib/src/my_module.dart",
+ "lib/src/modules/gates.dart"
+ ],
+ "modules": {
+ "Top": {
+ "outputFiles": {
+ "sv": ["Top.sv"],
+ "sc": ["Top.cpp"]
+ },
+ "tree": [
+ [
+ "0:6:20",
+ ["0:15:9", "a@sv:2:19,8:7;sc:44:5"],
+ ["0:16:15", "b@sv:3:20~originalB"],
+ ["1:22:3", "*inner@sv:7:1"]
+ ]
+ ]
+ }
+ }
+}
+```
+
+- **`version`** — v6 is the current format. The extension can still parse
+ v5; other explicit versions are rejected.
+- **`files`** — array of ROHD source paths, indexed by the first number in
+ each trie frame.
+- **`outputFiles`** — map from output language to generated file list, for
+ example `"sv": ["Top.sv"]` or `"sc": ["Top.cpp"]`. The first file for
+ each language is the canonical lookup target.
+- **`tree`** — list of trie root nodes. Each node starts with a source frame
+ string, then contains child nodes and/or symbol strings that share that
+ source-frame prefix.
+- **`"0:15:9"`** — ROHD source frame: file index 0, line 15, column 9.
+ The column is optional and defaults to 1.
+- **`"a@sv:2:19,8:7;sc:44:5"`** — signal `a`, with two SystemVerilog
+ output positions and one SystemC output position. Output-language groups
+ are separated by semicolons; entries within one language are separated by
+ commas. The language tag appears on the first entry in the group, so
+ `sv:2:19,8:7` means `sv:2:19` and `sv:8:7`.
+- **`"b@sv:3:20~originalB"`** — canonical signal name `b`, original source
+ name `originalB`. Lookups may use either name.
+- **`"*inner@sv:7:1"`** — submodule instance `inner`. Instance symbols are
+ prefixed with `*`; signal symbols are not.
+
+Source frames accumulate along the trie path from outermost to innermost.
+When the extension opens ROHD source frames, it presents them innermost first
+so the construction site closest to the signal or instance is selected first.
+
+## Commands
+
+| Command | Title |
+|---------|-------|
+| `rohd.openSourceLocation` | Go to Source Location |
+| `rohd.openSourceLocations` | Go to Source Locations (multi-frame) |
+| `rohd.nextSourceLocation` | Next Source Frame |
+| `rohd.prevSourceLocation` | Previous Source Frame |
+| `rohd.connectDtd` | Connect to Dart Tooling Daemon |
+| `rohd.showForwardedUris` | Show Forwarded DTD/VM URIs |
+
+## Settings
+
+| Setting | Default | Description |
+|---------|---------|-------------|
+| `rohd.enableCompletions` | *(unset)* | Enable context-aware ROHD completions. On first activation you are prompted; the choice is saved globally. Set `true`/`false` to skip the prompt. |
+| `rohd.dtdUri` | `""` | WebSocket URI of the Dart Tooling Daemon. Leave empty for auto-discovery. |
+
+## Prerequisites
+
+- **Node.js ≥ 18** (use nvm if the container ships an older version):
+
+ ```bash
+ export PATH="$HOME/.nvm/versions/node/v20.19.6/bin:$PATH"
+ node --version # v18+ or v20+
+ ```
+
+- **npm** (comes with Node)
+
+## Build
+
+```bash
+cd rohd_extension
+npm install
+npm run compile # produces out/extension.js
+```
+
+## Local Installation
+
+### Package as VSIX
+
+```bash
+cd rohd_extension
+yes | npx @vscode/vsce package --allow-missing-repository
+```
+
+This produces `rohd-0.3.0.vsix`.
+
+### Install
+
+```bash
+code --install-extension rohd-0.3.0.vsix --force
+```
+
+Then reload the VS Code window (**Developer: Reload Window**).
+
+### One-liner (build + install)
+
+```bash
+export PATH="$HOME/.nvm/versions/node/v20.19.6/bin:$PATH" \
+ && cd rohd_extension \
+ && npm install \
+ && npm run compile \
+ && rm -f *.vsix \
+ && yes | npx @vscode/vsce package --allow-missing-repository \
+ && code --install-extension rohd-0.3.0.vsix --force \
+ && echo "Done — reload the VS Code window to activate."
+```
+
+## Remote Installation (Dev Containers / SSH)
+
+Extensions that interact with the Dart debug adapter must be installed on the
+**remote** side (inside the container or on the SSH host).
+
+### Option 1: devcontainer.json (recommended)
+
+Place the extension source in your repo and build it on container creation:
+
+```jsonc
+// .devcontainer/devcontainer.json
+{
+ "postCreateCommand": "cd rohd_extension && export PATH=\"$HOME/.nvm/versions/node/v20.19.6/bin:$PATH\" && npm install && npm run compile && rm -f *.vsix && yes | npx @vscode/vsce package --allow-missing-repository && code --install-extension rohd-0.2.0.vsix --force"
+}
+```
+
+### Option 2: Pre-built VSIX
+
+Build the `.vsix` on your host or in CI, then install at container start:
+
+```jsonc
+{
+ "postStartCommand": "code --install-extension rohd_extension/rohd-0.2.0.vsix --force"
+}
+```
+
+### Option 3: Install via CLI while connected
+
+```bash
+code --install-extension rohd-0.2.0.vsix --force
+```
+
+The `code` CLI inside a remote session targets the VS Code Server
+automatically.
+
+### Option 4: Install via VS Code UI
+
+1. Connect to the remote host / container.
+2. Open Extensions (`Ctrl+Shift+X`).
+3. Click `...` → **Install from VSIX...** and select the `.vsix` file.
+4. Reload the window.
+
+### Option 5: Copy directly into `.vscode-server/extensions/`
+
+If the `code` CLI is not available (e.g. in a Dockerfile `RUN` step):
+
+```bash
+mkdir -p ~/.vscode-server/extensions/rohd.rohd-0.2.0
+cp -r rohd_extension/{package.json,out,snippets,resources} \
+ ~/.vscode-server/extensions/rohd.rohd-0.2.0/
+```
+
+The directory name must follow the pattern `.-`.
+
+## File Structure
+
+```text
+rohd_extension/
+├── package.json # Extension manifest
+├── tsconfig.json # TypeScript configuration
+├── src/
+│ ├── extension.ts # Entry point — activates all modules
+│ ├── source_navigator.ts # Cross-probe → editor navigation
+│ ├── dtd_bridge.ts # DTD JSON-RPC bridge
+│ ├── debug_tracker.ts # Debug adapter tracker (DTD/VM URIs)
+│ └── conditional_completions.ts # Context-aware conditional snippets
+├── out/ # Compiled JS (generated)
+├── snippets/
+│ └── rohd.json # ROHD Dart snippets
+└── resources/
+ └── icon.png # Extension icon
+```
+
+## License
+
+BSD-3-Clause — see the repository root LICENSE file.
diff --git a/rohd_extension/dart/analysis_options.yaml b/rohd_extension/dart/analysis_options.yaml
new file mode 100644
index 000000000..7c1aca46c
--- /dev/null
+++ b/rohd_extension/dart/analysis_options.yaml
@@ -0,0 +1 @@
+# include: package:lints/recommended.yaml
diff --git a/rohd_extension/dart/lib/dtd_service.dart b/rohd_extension/dart/lib/dtd_service.dart
new file mode 100644
index 000000000..47a540b72
--- /dev/null
+++ b/rohd_extension/dart/lib/dtd_service.dart
@@ -0,0 +1,147 @@
+// Copyright (C) 2026 Intel Corporation
+// SPDX-License-Identifier: BSD-3-Clause
+//
+// dtd_service.dart
+// DTD service handler for receiving cross-probe source navigation
+// requests from the ROHD DevTools extension.
+//
+// Registers a `rohd.goToSource` service on the Dart Tooling Daemon so
+// that the DevTools extension can send resolved SourceFrame lists for
+// navigation in the VS Code editor.
+//
+// 2026 April 27
+// Author: Desmond Kirkpatrick
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:json_rpc_2/json_rpc_2.dart';
+import 'package:web_socket_channel/web_socket_channel.dart';
+
+import 'source_navigator.dart';
+
+/// Callback invoked when the DTD service receives a goToSource request.
+///
+/// The TS shell provides this callback to bridge DTD requests into
+/// VS Code command execution.
+typedef GoToSourceCallback = Future Function(List frames,
+ {int startIndex});
+
+/// Manages the DTD connection and service registration for source
+/// navigation.
+class DtdService {
+ final GoToSourceCallback _onGoToSource;
+ Peer? _peer;
+ WebSocketChannel? _channel;
+ bool _disposed = false;
+
+ /// Creates a DTD service that calls [onGoToSource] when a
+ /// `rohd.goToSource` request arrives.
+ DtdService({required GoToSourceCallback onGoToSource})
+ : _onGoToSource = onGoToSource;
+
+ /// Connect to the DTD at [uri] and register the `rohd.goToSource`
+ /// service.
+ ///
+ /// Returns `true` if connection and registration succeeded.
+ Future connect(String uri) async {
+ if (_disposed) return false;
+
+ try {
+ _channel = WebSocketChannel.connect(Uri.parse(uri));
+ await _channel!.ready;
+ _peer = Peer(_channel!.cast());
+
+ // Register the service method.
+ _peer!.registerMethod('rohd.goToSource', _handleGoToSource);
+
+ // Start listening (non-blocking).
+ unawaited(
+ _peer!.listen().then((_) {
+ // Connection closed.
+ _peer = null;
+ }),
+ );
+
+ return true;
+ } on Exception catch (e) {
+ _peer = null;
+ _channel = null;
+ // ignore: avoid_print
+ print('[DtdService] Failed to connect to DTD at $uri: $e');
+ return false;
+ }
+ }
+
+ /// Whether the DTD connection is active.
+ bool get isConnected => _peer != null && !_peer!.isClosed;
+
+ /// Disconnect from DTD and clean up.
+ Future dispose() async {
+ _disposed = true;
+ await _peer?.close();
+ _peer = null;
+ await _channel?.sink.close();
+ _channel = null;
+ }
+
+ // ---------------------------------------------------------------------------
+ // RPC handler
+ // ---------------------------------------------------------------------------
+
+ /// Handle an incoming `rohd.goToSource` request.
+ ///
+ /// Expected parameters:
+ /// ```json
+ /// {
+ /// "frames": [
+ /// {"file": "lib/src/foo.dart", "line": 42, "col": 5, "type": "rohd"},
+ /// {"file": "Foo.sv", "line": 10, "col": 1, "type": "sv"}
+ /// ],
+ /// "index": 0 // optional starting frame
+ /// }
+ /// ```
+ Future