Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

virtuoso-bridge-lite

GitHub stars GitHub forks Clones Views

Open Issues Last Commit Website License: MIT PRs Welcome

English | 简体中文 | 繁體中文

A new infrastructure for Agentic Analog and Mixed-Signal Design. LLM Agents drive Cadence Virtuoso instances — locally or remotely — turning tedious handcrafting into automated design flows.

Why is this a "New Infrastructure"?

1. Deep Virtuoso Integration — Control across Schematic, Layout, Maestro, and Spectre.

  • Flexible programming: execute inline SKILL, load .il files, or use Python APIs
  • Four design domains: schematic editing, layout generation, simulation setup (Maestro), and standalone Spectre with PSF parsing
  • Deterministic schematic planning: explicit connectivity plus hard/soft grid, polarity-row, differential-pair, pin-column, and output-stage constraints

2. Scalable Architecture — Multi-server, multi-session, built for distributed design clusters.

  • Multi-profile SSH: connect to N design servers, each with independent tunnel
  • Run parallel simulations across servers and accounts
  • Verified across macOS, Windows, and Linux

3. AI-Native Design — Built for coding agents (Claude Code, Cursor, etc.) to drive Virtuoso.

  • CLI-first lifecycle and diagnostics: virtuoso-bridge start/status/restart
  • Ships with pre-defined agent skill files (skills/) — the agent knows how to use the bridge immediately
  • Optimized for high-frequency agent interactions with resilient, role-aware SSH routing

If you are an AI agent, read AGENTS.md first and follow its setup checklist.

Choose your setup

You want to...Use this pathNeeds
Drive Virtuoso on a remote EDA serverRemote modeSSH access, running Virtuoso, load(...) in CIW
Drive Virtuoso on the same machineLocal modeRunning Virtuoso, VB_REMOTE_HOST=localhost
Run Spectre from netlistsSpectre simulatorspectre on PATH, or VB_CADENCE_CSHRC
Run reproducible IC optimization workflowsOptimizer skill + optional external workflow CLISpectre/OCEAN setup, requirement files
Let a coding agent operate CadenceAgent skillsLink skills/ into your agent's skill directory

Virtuoso SKILL execution and Spectre simulation are independent. You can run Spectre without the SKILL bridge, and you can use the SKILL bridge without Spectre.

Python environment selection

Python entry points discover the nearest parent .env containing a bridge host role (VB_REMOTE_HOST, VB_GUI_HOST, VB_DAEMON_HOST, and related roles) or VB_LOCAL_PORT, then load it with override=True; this can change a long-lived process from local to remote mode. Pin the intended file before constructing a client when embedding the bridge:

from virtuoso_bridge.env import set_runtime_env_file

set_runtime_env_file("/path/to/virtuoso-bridge.env")

Quick Start

# 0. Get the source
git clone https://github.com/Arcadia-1/virtuoso-bridge-lite.git
cd virtuoso-bridge-lite

# 1. Install in a virtual environment
uv venv .venv
source .venv/bin/activate
uv pip install -e .

# 2. Create ~/.virtuoso-bridge/.env
virtuoso-bridge init user@host [-J user@jump-host]
# Or: virtuoso-bridge init      # empty template; edit VB_REMOTE_HOST yourself

# 3. Start and verify
virtuoso-bridge start          # starts tunnel and prints the CIW load(...) line
virtuoso-bridge status         # tunnel + Virtuoso daemon + Spectre availability

If the daemon is not loaded yet, the normal manual load(...) remains valid. For an opt-in X11 bootstrap, first select an explicit top-level CIW and then inject only the generated setup command:

virtuoso-bridge list-windows --top-level --json
virtuoso-bridge bootstrap --window 0x3000012

bootstrap refuses windows that are not identified as a CIW and does not accept arbitrary SKILL text.

Split GUI and daemon hosts

VB_REMOTE_HOST remains the simple one-host setting. In installations where Virtuoso runs on a GUI/login host but ipcBeginProcess() launches the daemon on a compute host, configure the roles explicitly:

VB_GUI_HOST=gui-host-a
VB_DEPLOY_HOST=gui-host-a
VB_DAEMON_HOST=compute-host-b
VB_SPECTRE_HOST=compute-host-b
VB_REMOTE_USER=user
VB_JUMP_HOST=gui-host-a

# Must be readable from the CIW and daemon host; /tmp is often isolated.
VB_REMOTE_SCRATCH_ROOT=/home/user/.virtuoso-bridge

The defaults are deployment = GUI and Spectre = legacy/daemon host. The shared jump host is suppressed automatically when the target itself is the jump host. status reads a daemon identity file written from the CIW banner, so it can diagnose a daemon/tunnel host mismatch even when the TCP endpoint is wrong.

On Windows PowerShell, replace the activation line with .\.venv\Scripts\Activate.ps1.

Single-connection SSH concurrency

OpenSSH ControlMaster is not usable with every Windows SSH build. For real multi-session multiplexing inside one bridge process, install the Paramiko backend and select it explicitly:

uv pip install -e '.[ssh]'
VB_SSH_BACKEND=paramiko
VB_SSH_MAX_SESSIONS=10
# Optional: route the first SSH hop through an unauthenticated SOCKS5 proxy.
VB_SSH_PROXY=socks5://127.0.0.1:10800

Commands and file transfers then share one authenticated target SSH Transport; each operation opens a channel on that Transport. Bulk and recursive transfers use the same tar plans as the OpenSSH backend; text uploads use the shared atomic staging plan, while single-file downloads use SFTP. VB_SSH_MAX_SESSIONS is a client-side gate and must be no greater than the target sshd MaxSessions. Waiting work queues at the gate instead of opening another command connection. There is no automatic fallback to independent SSH connections.

VB_SSH_PROXY is optional and applies only to Paramiko-backed commands and file transfers, including SFTP. It does not affect the OpenSSH -L tunnel created by virtuoso-bridge start; environments that also need this tunnel to traverse SOCKS5 must configure an OpenSSH ProxyCommand in the SSH config used by the bridge. VB_SSH_PROXY accepts an explicit socks5://host:port URL without credentials and asks the proxy to resolve the proxied first-hop hostname. For direct connections the proxy opens the target SSH socket. With VB_JUMP_HOST or one ProxyJump hop, it opens the jump-host socket and Paramiko reaches the target through that SSH transport. Proxy failures are reported directly; the bridge does not fall back to a direct or OpenSSH command connection. While the proxy is active, SSH config lookup forces CanonicalizeHostname=no so OpenSSH cannot resolve the proxied first-hop hostname locally before Paramiko opens the SOCKS5 connection.

The Paramiko backend reads VB_SSH_CONFIG when set, otherwise the normal user and system SSH config files. It honors Include, host aliases, HostName/User/Port, IdentityFile, IdentitiesOnly, and one ProxyJump hop. Explicit VB_REMOTE_*, VB_JUMP_*, and key arguments take precedence. ProxyCommand fails during initialization unless an explicit VB_SSH_PROXY overrides it. Chained ProxyJump routes and a missing explicit VB_SSH_CONFIG also fail instead of being silently ignored. Both the target and jump host must already have keys in the applicable user or system known_hosts files; unknown and changed keys are rejected. The backend honors UserKnownHostsFile, GlobalKnownHostsFile, and HostKeyAlias. Run the documented prerequisite ssh <host> echo ok once with OpenSSH to review and record a new host key before selecting Paramiko. Paramiko cannot enforce an OpenSSH RevokedHostKeys file or KRL, so any configured value other than none fails during initialization instead of silently bypassing revocation policy.

MaxStartups governs unauthenticated TCP handshakes, while MaxSessions governs channels on one authenticated connection. The Paramiko backend avoids one handshake per parallel job, but it cannot override the server's MaxSessions. The long-lived Virtuoso TCP port-forward created by virtuoso-bridge start remains a standalone, non-ControlMaster OpenSSH process because it must survive after the starting CLI and any shared command master exit.

from virtuoso_bridge import VirtuosoClient
client = VirtuosoClient.from_env()
client.execute_skill("1+2")  # VirtuosoResult(status=SUCCESS, output='3')

For fail-fast access to standalone Spectre PSF ASCII artifacts, use the strict helpers instead of guessing filenames, keys, or value shapes:

from pathlib import Path
from virtuoso_bridge.spectre import SpectreSimulator
from virtuoso_bridge.spectre.psf import frequency_hz, read_psf_ascii, result_file, vector

result = SpectreSimulator.from_env(work_dir="output").run_simulation("tb.scs")
if not result.ok:
    raise RuntimeError(result.errors)
raw_psf = Path(result.metadata["output_dir"])
ac = read_psf_ascii(result_file(raw_psf, "*.ac"))
frequency = frequency_hz(ac)
vout = vector(ac, "VOUT")  # exact raw PSF key; missing/invalid data raises

Useful first commands after the bridge is up:

virtuoso-bridge windows       # list all open Virtuoso windows
virtuoso-bridge screenshot    # screenshot CIW to the user artifact directory
virtuoso-bridge export-visio MyLib MyCell -o MyCell.vsdx  # Windows + Visio

…or skip Python entirely — run SKILL straight from the shell:

# One-liner — full VirtuosoResult JSON on stdout
virtuoso-bridge eval 'getCurrentTime()'

# Multi-line SKILL via heredoc (auto-wrapped in progn; returns the last form)
virtuoso-bridge eval --stdin <<'EOF'
let((libs)
  libs = mapcar(lambda((l) l~>name) ddGetLibList())
  printf("found %d libraries\n" length(libs))
  libs)
EOF

# Whole .il file — uploaded automatically in SSH mode
virtuoso-bridge load my_script.il

For detailed setup (jump hosts, multi-profile, local mode), see AGENTS.md.

CLI reference

All commands take -p PROFILE / --env PATH to pick a non-default config; run virtuoso-bridge <cmd> --help for full flags.

CommandWhat it does
Tunnel / lifecycle
init [user@host] [-J jump]Write a starter .env (no args = empty template)
start [--bind-venv]Start SSH tunnel + deploy daemon; --bind-venv (with -p X) also binds the active virtualenv to profile X
stopStop the SSH tunnel
restartRestart tunnel and refresh the deployed daemon setup
statusTunnel + daemon health + Spectre availability
licenseCheck Spectre license availability
Profile binding
profile showPrint the resolved profile, its source, and the active venv binding path
profile bind PROFILE --venvPin the active virtualenv to PROFILE (naked from_env() calls in that venv resolve to it)
profile clear --venvRemove the venv binding
SKILL execution
load FILE.ilRun a .il file in Virtuoso (uploads it in SSH mode). VS Code task–friendly; outputs VirtuosoResult JSON
eval 'EXPR' / eval --stdinRun an inline SKILL expression; supports multi-statement via auto-wrapped progn(...)
Interaction / diagnostics
windowsList all open Virtuoso windows (number + name)
screenshot [ciw|current|N] [-o DIR|FILE]Capture a window; defaults to the user artifact screenshots directory
dismiss-dialogX11 path: find and dismiss blocking GUI dialogs (saves you when SKILL channel deadlocks on a modal)
list-windows [--top-level] [--json]X11 path: enumerate Virtuoso windows; --top-level returns one deduplicated entry per frame for CIW selection
bootstrap --window WINDOW_IDOpt-in X11 first load: inject only the generated load(...) into one explicit, verified CIW
dismiss-window WINDOW_ID [--action enter|escape|alt-y|alt-n]X11 path: send an explicit action to one window ID returned by list-windows
snapshot [-o DIR] [--history H]Dump the focused Virtuoso window (maestro/schematic/...) — brief by default, full disk dump with -o
Export
export-visio LIB CELL -o OUT.vsdxRender a Virtuoso schematic to Microsoft Visio (Windows + pywin32)
SKILL Finder
skill-find <query>Search SKILL functions by name (fuzzy/prefix/suffix/exact/regex)
skill-info <fn>Get detailed More Info docs for a SKILL function
doc-search <query>Search installed Cadence documentation through the active bridge, or pass --doc-root for local/offline search

Snapshot a maestro run

Pull the currently-focused maestro session's setup + latest-run artifacts to a local folder:

virtuoso-bridge snapshot -o output                       # auto-picks newest history
virtuoso-bridge snapshot -o output --history Interactive.160   # pin a specific history

Output tree (one example):

output/20260422_142137__MyLib__myTB/
├── maestro.sdb, active.state                    # raw Cadence files
├── state_from_sdb.xml, state_from_active_state.xml  # filtered, high-signal
├── state_from_skill.txt                         # SKILL-probe setup summary
└── Interactive.N/
    ├── Interactive.N.{log,rdb,msg.db}           # run-level (rdb = SQLite)
    └── <pt>/<tb>/
        ├── netlist/   → netlist, input.scs, qpInformation.ils, exprOutputs.json, paramInfo.ils
        └── psf/       → spectre.out, logFile, dcOp.dc, *.ac, *.tran, ...

Per-point netlist/ keeps only the 5 files that describe the runnable design and outputs (main SPICE netlist, testbench top level, SKILL and JSON output-expression definitions, corner label). Psf keeps stdout + logs + non-binary analysis results. The full rule set — including what's commented out and why — lives in src/virtuoso_bridge/virtuoso/maestro/snapshot_filter.yaml; edit the YAML (uncomment / comment lines) to add or drop files, no code change needed. Binary waveforms (*.raw, wavedb/) are never pulled — read scalar results through client.maestro.read_results() instead.

Exposing skills to your coding agent

The skills/ directory ships Claude Code skills (virtuoso, spectre, netlist, optimizer). They are not symlinked into the repo's .claude/skills/ on purpose — repo-tracked symlinks break on Windows and hardcode one user's absolute paths. Instead, each user links them into their own ~/.claude/skills/ once after cloning:

# macOS / Linux
mkdir -p ~/.claude/skills
ln -s "$(pwd)/skills/virtuoso"  ~/.claude/skills/virtuoso
ln -s "$(pwd)/skills/spectre"   ~/.claude/skills/spectre
ln -s "$(pwd)/skills/netlist"   ~/.claude/skills/netlist
ln -s "$(pwd)/skills/optimizer" ~/.claude/skills/optimizer
# Windows (PowerShell, Developer Mode or elevated shell)
New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\skills" | Out-Null
New-Item -ItemType SymbolicLink -Path "$env:USERPROFILE\.claude\skills\virtuoso"  -Target "$PWD\skills\virtuoso"
New-Item -ItemType SymbolicLink -Path "$env:USERPROFILE\.claude\skills\spectre"   -Target "$PWD\skills\spectre"
New-Item -ItemType SymbolicLink -Path "$env:USERPROFILE\.claude\skills\netlist"   -Target "$PWD\skills\netlist"
New-Item -ItemType SymbolicLink -Path "$env:USERPROFILE\.claude\skills\optimizer" -Target "$PWD\skills\optimizer"

Cursor and other agents that load skills from a user-level directory follow the same pattern — point their skills path at skills/ in this repo.

Architecture

Architecture

  • VirtuosoClient — pure TCP SKILL client. Sends SKILL as JSON, gets results. No SSH awareness.
  • SpectreSimulator — runs standalone Spectre simulations locally or through SSH, then parses PSF ASCII results into Python data.
  • SSHClient — resolves GUI, deployment, daemon, and Spectre host roles; maintains a standalone TCP port-forward to the daemon host and uses OpenSSH or process-local Paramiko transports for role-specific commands and files. Optional — bypassed in local mode.

Fully decoupled: VirtuosoClient works with any TCP endpoint — SSH tunnel, VPN, direct LAN, or local. Multiple connection profiles are supported, each managing an independent tunnel to a separate design server.

Want to understand the raw mechanism? Start with src/virtuoso_bridge/virtuoso/basic/resources/ramic_bridge.il and src/virtuoso_bridge/virtuoso/basic/bridge.py.

Want to use Virtuoso locally without SSH? See Local mode in AGENTS.md.

Comparison with skillbridge

Featurevirtuoso-bridge-liteskillbridge
Core mechanismipcBeginProcess + evalstringipcBeginProcess + evalstring
Local modeYesYes
Remote executionSSH tunnel, jump host, auto-reconnectNot supported
Calling styleString-based: execute_skill("dbOpenCellViewByType(...)")Pythonic mapping: ws.db.open_cell_view_by_type(...)
Load .il filesclient.load_il()Not supported
Layout / schematic APIclient.layout.create() / modify() context managersRaw SKILL only
Spectre simulationBuilt-in runner + PSF parserNot supported
AI agent supportSkill files, CLI-first, command loggingNot designed for agents
Python ↔ SKILL typesString-basedAuto bidirectional mapping
IDE tab completionNo (not needed by agents)Yes (Jupyter, PyCharm stubs)

In short: Both projects are built on the same Cadence SKILL IPC facility, using the same core mechanism: ipcBeginProcess + evalstring + ipcWriteProcess. Here are the core lines from each:

virtuoso-bridge-litesrc/virtuoso_bridge/virtuoso/basic/resources/ramic_bridge.il
RBIpc = ipcBeginProcess(
  sprintf(nil "%s %L %L %L" RBPython RBDPath host RBPort)
  "" 'RBIpcDataHandler 'RBIpcErrHandler 'RBIpcFinishHandler "")

procedure(RBIpcDataHandler(ipcId data)
  if(errset(result = evalstring(data)) then
    ipcWriteProcess(ipcId sprintf(nil "%c%L%c" 2 result 30))
  else
    ipcWriteProcess(ipcId sprintf(nil "%c%L%c" 21 errset.errset 30))
  )
)
skillbridgeskillbridge/server/python_server.il
pyStartServer.ipc = ipcBeginProcess(
  executableWithArgs "" '__pyOnData '__pyOnError '__pyOnFinish pyStartServer.logName)

defun(__pyOnData (id data)
  foreach(line parseString(data "\n")
    capturedWarning = __pyCaptureWarnings(errset(result=evalstring(line)))
    ipcWriteProcess(id lsprintf("success %L\n" result))
  )
)

The divergence is in what's built on top: skillbridge stays thin — a Pythonic RPC client for interactive local use. virtuoso-bridge-lite adds SSH remote access, high-level layout/schematic APIs, Spectre simulation, and an AI-agent-ready harness.

Citation

If you use virtuoso-bridge in academic work, please cite:

@article{zhang2025virtuosobridge,
  title   = {Virtuoso-Bridge: An Agent-Native Bridge for Remote Analog and Mixed-Signal Design Automation},
  author  = {Zhang, Zhishuai and Li, Xintian and Sun, Nan and Jie, Lu},
  year    = {2025}
}

Authors

  • Zhishuai Zhang — Tsinghua University
  • Xintian Li — Tsinghua University
  • Nan Sun — Tsinghua University
  • Lu Jie — Tsinghua University

关于 About

Bridge between LLM-Agent and Cadence Virtuoso. A new infrastructure for Agentic Analog and Mixed-Signal Design.

语言 Languages

Python98.4%
IL Assembly1.5%
Shell0.1%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
672
Total Commits
峰值: 204次/周
Less
More

核心贡献者 Contributors