rocket-engine 0.0.2b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rocket_engine/__init__.py +8 -0
- rocket_engine/__main__.py +12 -0
- rocket_engine/adapters/README.md +54 -0
- rocket_engine/adapters/__init__.py +7 -0
- rocket_engine/adapters/api.py +39 -0
- rocket_engine/adapters/base.py +46 -0
- rocket_engine/adapters/cace/__init__.py +163 -0
- rocket_engine/adapters/circulax/README.md +10 -0
- rocket_engine/adapters/circulax/__init__.py +112 -0
- rocket_engine/adapters/circulax/_driver.py +190 -0
- rocket_engine/adapters/circulax/adapter.py +538 -0
- rocket_engine/adapters/circulax/results.py +291 -0
- rocket_engine/adapters/cli/README.md +34 -0
- rocket_engine/adapters/cli/__init__.py +305 -0
- rocket_engine/adapters/connectivity/__init__.py +196 -0
- rocket_engine/adapters/connectivity/results.py +214 -0
- rocket_engine/adapters/cosim/README.md +9 -0
- rocket_engine/adapters/cosim/__init__.py +1390 -0
- rocket_engine/adapters/cosim/results.py +214 -0
- rocket_engine/adapters/devsim/__init__.py +171 -0
- rocket_engine/adapters/digital_flow/README.md +9 -0
- rocket_engine/adapters/digital_flow/__init__.py +1121 -0
- rocket_engine/adapters/digital_flow/results.py +227 -0
- rocket_engine/adapters/digital_flow/state.py +462 -0
- rocket_engine/adapters/drc/README.klayout.md +7 -0
- rocket_engine/adapters/drc/README.md +7 -0
- rocket_engine/adapters/drc/__init__.py +1 -0
- rocket_engine/adapters/drc/klayout.py +952 -0
- rocket_engine/adapters/drc/magic.py +287 -0
- rocket_engine/adapters/drc/results.py +184 -0
- rocket_engine/adapters/drc/results_kl.py +188 -0
- rocket_engine/adapters/elmer/__init__.py +163 -0
- rocket_engine/adapters/envelope.py +120 -0
- rocket_engine/adapters/equivalence/__init__.py +156 -0
- rocket_engine/adapters/errors.py +181 -0
- rocket_engine/adapters/extract_magic/README.md +15 -0
- rocket_engine/adapters/extract_magic/__init__.py +425 -0
- rocket_engine/adapters/extract_magic/results.py +209 -0
- rocket_engine/adapters/formal_sby/README.md +7 -0
- rocket_engine/adapters/formal_sby/__init__.py +773 -0
- rocket_engine/adapters/formal_sby/formal_edge.py +195 -0
- rocket_engine/adapters/formal_sby/results.py +185 -0
- rocket_engine/adapters/gdsfactory/__init__.py +194 -0
- rocket_engine/adapters/gdsfactory/_driver.py +459 -0
- rocket_engine/adapters/gdsfactory/adapter.py +609 -0
- rocket_engine/adapters/gdsfactory/results.py +181 -0
- rocket_engine/adapters/hdl_sim/README.md +7 -0
- rocket_engine/adapters/hdl_sim/__init__.py +369 -0
- rocket_engine/adapters/hdl_sim/ghdl.py +155 -0
- rocket_engine/adapters/hdl_sim/results.py +178 -0
- rocket_engine/adapters/hdl_sim/verilator.py +167 -0
- rocket_engine/adapters/http/README.md +15 -0
- rocket_engine/adapters/http/__init__.py +458 -0
- rocket_engine/adapters/http/events.py +63 -0
- rocket_engine/adapters/ipc/README.md +9 -0
- rocket_engine/adapters/ipc/__init__.py +111 -0
- rocket_engine/adapters/lvs_netgen/README.md +20 -0
- rocket_engine/adapters/lvs_netgen/__init__.py +889 -0
- rocket_engine/adapters/lvs_netgen/results.py +168 -0
- rocket_engine/adapters/mcp/README.md +19 -0
- rocket_engine/adapters/mcp/__init__.py +85 -0
- rocket_engine/adapters/mcp/api-schema.json +6107 -0
- rocket_engine/adapters/mcp/pull.py +246 -0
- rocket_engine/adapters/mcp/server.py +794 -0
- rocket_engine/adapters/meep/__init__.py +167 -0
- rocket_engine/adapters/meow/README.md +7 -0
- rocket_engine/adapters/meow/__init__.py +190 -0
- rocket_engine/adapters/meow/_driver.py +227 -0
- rocket_engine/adapters/meow/_envelope.py +108 -0
- rocket_engine/adapters/meow/adapter.py +491 -0
- rocket_engine/adapters/meow/results.py +218 -0
- rocket_engine/adapters/mutation/__init__.py +192 -0
- rocket_engine/adapters/ngspice/README.md +28 -0
- rocket_engine/adapters/ngspice/__init__.py +627 -0
- rocket_engine/adapters/ngspice/results.py +215 -0
- rocket_engine/adapters/postlayout/README.md +28 -0
- rocket_engine/adapters/postlayout/__init__.py +17 -0
- rocket_engine/adapters/postlayout/adapter.py +1062 -0
- rocket_engine/adapters/postlayout/results.py +330 -0
- rocket_engine/adapters/postlayout/translate.py +71 -0
- rocket_engine/adapters/profile.py +220 -0
- rocket_engine/adapters/sax/README.md +23 -0
- rocket_engine/adapters/sax/__init__.py +516 -0
- rocket_engine/adapters/sax/_driver.py +148 -0
- rocket_engine/adapters/sax/results.py +291 -0
- rocket_engine/adapters/stdio_console.py +1151 -0
- rocket_engine/adapters/syntax/README.md +5 -0
- rocket_engine/adapters/syntax/__init__.py +366 -0
- rocket_engine/adapters/syntax/results.py +181 -0
- rocket_engine/adapters/synth_yosys/README.md +7 -0
- rocket_engine/adapters/synth_yosys/__init__.py +655 -0
- rocket_engine/adapters/synth_yosys/amaranth.py +120 -0
- rocket_engine/adapters/synth_yosys/ghdl.py +128 -0
- rocket_engine/adapters/synth_yosys/results.py +173 -0
- rocket_engine/adapters/timing_sta/README.md +9 -0
- rocket_engine/adapters/timing_sta/__init__.py +726 -0
- rocket_engine/adapters/view_sync/__init__.py +185 -0
- rocket_engine/adapters/view_sync/results.py +189 -0
- rocket_engine/adapters/vlsir/__init__.py +142 -0
- rocket_engine/adapters/xschem/__init__.py +440 -0
- rocket_engine/adapters/xschem/deck.py +266 -0
- rocket_engine/clients/__init__.py +5 -0
- rocket_engine/clients/runtime.py +255 -0
- rocket_engine/composition.py +2279 -0
- rocket_engine/execution/README.md +30 -0
- rocket_engine/execution/__init__.py +150 -0
- rocket_engine/execution/apig/__init__.py +1 -0
- rocket_engine/execution/apig/pool.py +463 -0
- rocket_engine/execution/apig/submit.py +124 -0
- rocket_engine/execution/block_runs_scope.py +479 -0
- rocket_engine/execution/coalesce.py +414 -0
- rocket_engine/execution/console/__init__.py +1 -0
- rocket_engine/execution/console/apply_session.py +318 -0
- rocket_engine/execution/console/lifecycle.py +265 -0
- rocket_engine/execution/dispatch.py +205 -0
- rocket_engine/execution/errors.py +31 -0
- rocket_engine/execution/ports.py +797 -0
- rocket_engine/execution/pyexec/__init__.py +1 -0
- rocket_engine/execution/pyexec/apply_context.py +547 -0
- rocket_engine/execution/pyexec/approvals.py +173 -0
- rocket_engine/execution/pyexec/runner.py +404 -0
- rocket_engine/execution/pyexec/support.py +464 -0
- rocket_engine/execution/python_exec.py +897 -0
- rocket_engine/execution/queue.py +85 -0
- rocket_engine/execution/replay_safe.py +601 -0
- rocket_engine/execution/routing/__init__.py +1 -0
- rocket_engine/execution/routing/calls.py +364 -0
- rocket_engine/execution/routing/namespace.py +785 -0
- rocket_engine/execution/routing/ports.py +102 -0
- rocket_engine/execution/routing/tools.py +346 -0
- rocket_engine/execution/runner.py +311 -0
- rocket_engine/execution/runtime.py +1149 -0
- rocket_engine/execution/service.py +279 -0
- rocket_engine/execution/worker.py +464 -0
- rocket_engine/interfaces/README.md +26 -0
- rocket_engine/interfaces/__init__.py +58 -0
- rocket_engine/interfaces/apig_assets.py +407 -0
- rocket_engine/interfaces/celllink.py +461 -0
- rocket_engine/interfaces/documents.py +92 -0
- rocket_engine/interfaces/envelopes.py +131 -0
- rocket_engine/interfaces/errors.py +16 -0
- rocket_engine/interfaces/layout.py +410 -0
- rocket_engine/interfaces/layout_schema.yaml +278 -0
- rocket_engine/interfaces/paths.py +223 -0
- rocket_engine/interfaces/pdks.py +550 -0
- rocket_engine/interfaces/ports.py +72 -0
- rocket_engine/interfaces/references.py +115 -0
- rocket_engine/interfaces/refs.py +85 -0
- rocket_engine/interfaces/runparams.py +304 -0
- rocket_engine/interfaces/setup.py +552 -0
- rocket_engine/interfaces/setup_catalogue.yaml +243 -0
- rocket_engine/interfaces/setup_probes.py +66 -0
- rocket_engine/interfaces/vocabulary.py +58 -0
- rocket_engine/interfaces/yaml_dump.py +31 -0
- rocket_engine/library/README.md +22 -0
- rocket_engine/library/__init__.py +13 -0
- rocket_engine/library/apig_catalog.py +371 -0
- rocket_engine/library/catalog.py +185 -0
- rocket_engine/library/errors.py +16 -0
- rocket_engine/mutate/__init__.py +776 -0
- rocket_engine/mutate/operators.py +700 -0
- rocket_engine/ops/__init__.py +1 -0
- rocket_engine/ops/deferred.py +279 -0
- rocket_engine/ops/editors.py +404 -0
- rocket_engine/ops/evidence.py +194 -0
- rocket_engine/ops/gates.py +262 -0
- rocket_engine/ops/ports.py +76 -0
- rocket_engine/ops/queries.py +417 -0
- rocket_engine/ops/runs.py +553 -0
- rocket_engine/ops/submit.py +614 -0
- rocket_engine/project/README.md +27 -0
- rocket_engine/project/__init__.py +6 -0
- rocket_engine/project/canvas/__init__.py +55 -0
- rocket_engine/project/canvas/compile.py +109 -0
- rocket_engine/project/canvas/integrate.py +48 -0
- rocket_engine/project/canvas/schema.py +241 -0
- rocket_engine/project/canvas/templates.py +46 -0
- rocket_engine/project/canvas/validate.py +603 -0
- rocket_engine/project/edit/__init__.py +5 -0
- rocket_engine/project/edit/cas.py +87 -0
- rocket_engine/project/edit/transactions.py +282 -0
- rocket_engine/project/errors.py +16 -0
- rocket_engine/project/frontend13.py +685 -0
- rocket_engine/project/geometry/__init__.py +23 -0
- rocket_engine/project/geometry/ops.py +451 -0
- rocket_engine/project/geometry/snap.py +103 -0
- rocket_engine/project/ip/__init__.py +1 -0
- rocket_engine/project/ip/blocks.py +195 -0
- rocket_engine/project/ip/entry.schema.json +78 -0
- rocket_engine/project/ip/lifecycle.py +402 -0
- rocket_engine/project/ip/verbs.py +123 -0
- rocket_engine/project/layers/__init__.py +6 -0
- rocket_engine/project/layers/bindings.py +115 -0
- rocket_engine/project/layers/catalog.py +228 -0
- rocket_engine/project/layers/presets.py +43 -0
- rocket_engine/project/ledger/__init__.py +1 -0
- rocket_engine/project/ledger/docs.py +167 -0
- rocket_engine/project/ledger/entities.py +176 -0
- rocket_engine/project/ledger/events.py +29 -0
- rocket_engine/project/ledger/runs.py +257 -0
- rocket_engine/project/ledger/shared.py +174 -0
- rocket_engine/project/ledger/snapshots.py +385 -0
- rocket_engine/project/ledger/wires.py +552 -0
- rocket_engine/project/process/__init__.py +14 -0
- rocket_engine/project/process/model.py +193 -0
- rocket_engine/project/process/recipes.py +121 -0
- rocket_engine/project/schematic/__init__.py +1 -0
- rocket_engine/project/schematic/hierarchy.py +312 -0
- rocket_engine/project/schematic/instances.py +393 -0
- rocket_engine/project/schematic/sheets.py +198 -0
- rocket_engine/project/service.py +2941 -0
- rocket_engine/project/symbols/__init__.py +1 -0
- rocket_engine/project/symbols/cells.py +324 -0
- rocket_engine/project/symbols/linkage.py +356 -0
- rocket_engine/project/symbols/symbols.py +352 -0
- rocket_engine/project/templates/__init__.py +303 -0
- rocket_engine/project/templates/errors.py +25 -0
- rocket_engine/project/testbench/__init__.py +49 -0
- rocket_engine/project/testbench/blocks.py +619 -0
- rocket_engine/project/testbench/templates.py +134 -0
- rocket_engine/stores/README.md +12 -0
- rocket_engine/stores/__init__.py +6 -0
- rocket_engine/stores/ecs.py +1508 -0
- rocket_engine/stores/layers.py +1023 -0
- rocket_engine/stores/settings.py +1188 -0
- rocket_engine/stores/wireplace.py +1324 -0
- rocket_engine/workflows/README.md +18 -0
- rocket_engine/workflows/__init__.py +53 -0
- rocket_engine/workflows/capability_plan.py +102 -0
- rocket_engine/workflows/completion.py +228 -0
- rocket_engine/workflows/connectgraph.py +734 -0
- rocket_engine/workflows/connectgraph_emit.py +336 -0
- rocket_engine/workflows/gates.py +144 -0
- rocket_engine/workflows/photonic_wire.py +76 -0
- rocket_engine/workflows/stages.py +324 -0
- rocket_engine/workflows/viewsync.py +399 -0
- rocket_engine-0.0.2b1.dist-info/METADATA +200 -0
- rocket_engine-0.0.2b1.dist-info/RECORD +241 -0
- rocket_engine-0.0.2b1.dist-info/WHEEL +4 -0
- rocket_engine-0.0.2b1.dist-info/entry_points.txt +2 -0
- rocket_engine-0.0.2b1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
"""Xschem schematic deck adapter (DECK-XSCHEM): netlist, then sim.
|
|
2
|
+
|
|
3
|
+
Protocol, verified against XSCHEM V3.4.8 on this host: batch
|
|
4
|
+
netlist is `xschem -x -q --rcfile <rc> -s -n <sch> -o <dir>
|
|
5
|
+
-N <name>` (add `-f` for a flat netlist, `-S` when the schematic
|
|
6
|
+
owns the sim, `--command/--script` for scripted flows). Project
|
|
7
|
+
rcfiles may hardcode `XSCHEM_SHAREDIR` to a path that does not
|
|
8
|
+
exist on the run host (a fixture xschemrc pointed at
|
|
9
|
+
`/usr/share/xschem` while the install lives under
|
|
10
|
+
`/usr/local`); the batch then exits 0 with no output and only a
|
|
11
|
+
`Tcl_AppInit() err` line in the log — silent-fail. The adapter
|
|
12
|
+
wraps the project rcfile: it pins `XSCHEM_SHAREDIR` to the real
|
|
13
|
+
install around the project source and re-pins after it, detects
|
|
14
|
+
the init error loudly, and refuses a missing netlist output
|
|
15
|
+
instead of fake-greening. `DISPLAY` is scrubbed so a dead remote
|
|
16
|
+
display can never pop (or hang) a GUI. Netgen-style: the verdict
|
|
17
|
+
always comes from artifacts and logs, never the exit code; a
|
|
18
|
+
missing binary fails with the PROV remedy. `** sch_path` /
|
|
19
|
+
`** sym_path` headers embed the checkout path, so netlists
|
|
20
|
+
normalize to basenames before any byte-compare.
|
|
21
|
+
|
|
22
|
+
Engine wiring (vocabulary operation, composition/MCP
|
|
23
|
+
registration) belongs to the owning surface; this file staffs
|
|
24
|
+
the solver with operation `run_xschem`.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import re
|
|
28
|
+
import shutil
|
|
29
|
+
import subprocess
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
from ...execution.errors import ExecutionError
|
|
33
|
+
from ...interfaces import ToolAdapter
|
|
34
|
+
from ...interfaces.runparams import MAX_TIMEOUT_S, MIN_TIMEOUT_S
|
|
35
|
+
from ..errors import AdapterError
|
|
36
|
+
|
|
37
|
+
OPERATION = "run_xschem"
|
|
38
|
+
_TIMEOUT_S = 120
|
|
39
|
+
_INIT_ERR_RE = re.compile(r"Tcl_AppInit\(\) err|cannot find .*xschem\.tcl")
|
|
40
|
+
_VERSION_RE = re.compile(r"XSCHEM V3")
|
|
41
|
+
_SCH_PATH_RE = re.compile(r"^(\*\* (?:sch_path|sym_path):\s*).*[/\\]([^/\\\s]+)\s*$", re.M)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _budget_or_raise(job):
|
|
45
|
+
"""Job solver budget: validated, or the adapter default.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
job: Solver job, maybe with a `timeout_s`.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Budget seconds for every `_run` call.
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
AdapterError: invalid_command for a boolean,
|
|
55
|
+
non-number, or out-of-bounds budget.
|
|
56
|
+
"""
|
|
57
|
+
timeout = job.get("timeout_s", _TIMEOUT_S)
|
|
58
|
+
if (
|
|
59
|
+
isinstance(timeout, bool)
|
|
60
|
+
or not isinstance(timeout, (int, float))
|
|
61
|
+
or not MIN_TIMEOUT_S <= timeout <= MAX_TIMEOUT_S
|
|
62
|
+
):
|
|
63
|
+
raise AdapterError(
|
|
64
|
+
"invalid_command", f"Bad timeout_s: {timeout!r}."
|
|
65
|
+
)
|
|
66
|
+
return timeout
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def normalize_netlist(text):
|
|
70
|
+
"""Strip checkout paths from xschem path headers.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
text: Raw netlist text with `** sch_path` / `** sym_path`
|
|
74
|
+
headers carrying absolute checkout paths.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Netlist text with those headers reduced to basenames,
|
|
78
|
+
byte-comparable across checkouts.
|
|
79
|
+
"""
|
|
80
|
+
return _SCH_PATH_RE.sub(r"\1\2", text)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def resolve_xschem(xschem_bin=None):
|
|
84
|
+
"""Path to a usable xschem binary, or None.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
xschem_bin: Explicit binary path, or None for PATH.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Binary path, or None when missing or not a file.
|
|
91
|
+
"""
|
|
92
|
+
if xschem_bin is not None:
|
|
93
|
+
return xschem_bin if Path(xschem_bin).is_file() else None
|
|
94
|
+
return shutil.which("xschem")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def sharedir_for(binary):
|
|
98
|
+
"""Install sharedir next to the binary, or None when absent.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
binary: Resolved xschem binary path.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
`<bindir>/../share/xschem` when it holds `xschem.tcl`,
|
|
105
|
+
else None.
|
|
106
|
+
"""
|
|
107
|
+
candidate = Path(binary).resolve().parent.parent / "share" / "xschem"
|
|
108
|
+
if (candidate / "xschem.tcl").is_file():
|
|
109
|
+
return str(candidate)
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class XschemAdapter(ToolAdapter):
|
|
114
|
+
"""run_xschem via the xschem batch netlister.
|
|
115
|
+
|
|
116
|
+
Serves the run_xschem operation with deck.tool `xschem` (the
|
|
117
|
+
only tool). Netlists a schematic to SPICE with a pinned
|
|
118
|
+
rcfile and explicit outputs; optional `-f` flat mode, `-S`
|
|
119
|
+
schematic-owned sim, and `--command`/`--script` seams.
|
|
120
|
+
|
|
121
|
+
Job keys:
|
|
122
|
+
sch_file (required): Schematic file under netlist.
|
|
123
|
+
rcfile: Project rcfile path (wrapped to pin
|
|
124
|
+
XSCHEM_SHAREDIR; absent means the install default).
|
|
125
|
+
output_name: Netlist file name (default
|
|
126
|
+
`<stem>.spice`).
|
|
127
|
+
flat: Flat netlist (`-f`) when true.
|
|
128
|
+
sim: Run the schematic-owned sim (`-S`) when true.
|
|
129
|
+
command: Extra `--command` Tcl snippet.
|
|
130
|
+
script: Extra `--script` Tcl file path.
|
|
131
|
+
workdir: Run directory (default ".").
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
runner: Optional isolated runner; direct subprocess when None.
|
|
135
|
+
xschem_bin: Explicit xschem binary; PATH when None.
|
|
136
|
+
|
|
137
|
+
Caution: xschem exits 0 on silent init failure and on
|
|
138
|
+
netlist errors alike — the verdict always comes from the
|
|
139
|
+
output file and the log, never the exit code.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
def __init__(self, runner=None, xschem_bin=None):
|
|
143
|
+
self._runner = runner
|
|
144
|
+
self._xschem_bin = xschem_bin
|
|
145
|
+
|
|
146
|
+
def capabilities(self, target):
|
|
147
|
+
"""Report the served operation, tool, and modes.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
target: Target the capabilities are reported for
|
|
151
|
+
(accepted, unused — one schematic per job).
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Capability mapping with operations, tool, and modes.
|
|
155
|
+
"""
|
|
156
|
+
return {
|
|
157
|
+
"operations": [OPERATION],
|
|
158
|
+
"tool": "xschem",
|
|
159
|
+
"modes": ["netlist", "sim"],
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
def _assert_version(self, banner):
|
|
163
|
+
"""Reject non-V3 xschem banners loudly.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
banner: Combined stdout/stderr of `xschem -v`.
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
AdapterError: missing_dependency when the banner is
|
|
170
|
+
not the V3 family this adapter is verified on.
|
|
171
|
+
"""
|
|
172
|
+
if not _VERSION_RE.search(banner or ""):
|
|
173
|
+
raise AdapterError(
|
|
174
|
+
"missing_dependency",
|
|
175
|
+
f"xschem V3 required, got banner: {(banner or '').strip()[:200]!r} "
|
|
176
|
+
"(PROV: install xschem V3, see docs/tickets/PROV-DEFERRED.md).",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def preflight(self, job):
|
|
180
|
+
"""Check operation, files, binary, version, and sharedir.
|
|
181
|
+
|
|
182
|
+
Cheap deterministic checks first (operation, files),
|
|
183
|
+
machine-dependent ones last (binary, version, sharedir).
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
job: Solver job with operation run_xschem plus
|
|
187
|
+
sch_file and deck inputs.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Checked mapping with sch_file, binary, sharedir,
|
|
191
|
+
output_name, flags, and timeout.
|
|
192
|
+
|
|
193
|
+
Raises:
|
|
194
|
+
AdapterError: unsupported_operation for another
|
|
195
|
+
operation, invalid_reference for a missing
|
|
196
|
+
schematic/rcfile/script, missing_dependency for
|
|
197
|
+
a missing binary, a non-V3 banner, or an
|
|
198
|
+
unresolvable sharedir.
|
|
199
|
+
"""
|
|
200
|
+
if job.get("operation") != OPERATION:
|
|
201
|
+
raise AdapterError(
|
|
202
|
+
"unsupported_operation",
|
|
203
|
+
f"xschem handles {OPERATION}, got {job.get('operation')}.",
|
|
204
|
+
)
|
|
205
|
+
timeout = _budget_or_raise(job)
|
|
206
|
+
sch_file = job.get("sch_file")
|
|
207
|
+
if not sch_file or not Path(sch_file).is_file():
|
|
208
|
+
raise AdapterError(
|
|
209
|
+
"invalid_reference", f"Schematic file not found: {sch_file}."
|
|
210
|
+
)
|
|
211
|
+
for key in ("rcfile", "script"):
|
|
212
|
+
path = job.get(key)
|
|
213
|
+
if path is not None and not Path(path).is_file():
|
|
214
|
+
raise AdapterError(
|
|
215
|
+
"invalid_reference", f"xschem {key} not found: {path}."
|
|
216
|
+
)
|
|
217
|
+
binary = resolve_xschem(self._xschem_bin)
|
|
218
|
+
if binary is None:
|
|
219
|
+
raise AdapterError(
|
|
220
|
+
"missing_dependency",
|
|
221
|
+
"xschem binary not found "
|
|
222
|
+
"(PROV: install xschem V3, see docs/tickets/PROV-DEFERRED.md).",
|
|
223
|
+
)
|
|
224
|
+
banner = self._version_banner(binary, timeout)
|
|
225
|
+
self._assert_version(banner)
|
|
226
|
+
sharedir = sharedir_for(binary)
|
|
227
|
+
if sharedir is None:
|
|
228
|
+
raise AdapterError(
|
|
229
|
+
"missing_dependency",
|
|
230
|
+
f"xschem sharedir unresolvable next to {binary} "
|
|
231
|
+
"(expected <bindir>/../share/xschem/xschem.tcl).",
|
|
232
|
+
)
|
|
233
|
+
output_name = job.get("output_name") or (Path(sch_file).stem + ".spice")
|
|
234
|
+
if not isinstance(output_name, str) or not output_name or "/" in output_name:
|
|
235
|
+
raise AdapterError(
|
|
236
|
+
"invalid_command", f"Bad xschem output_name: {output_name!r}."
|
|
237
|
+
)
|
|
238
|
+
return {
|
|
239
|
+
"sch_file": sch_file,
|
|
240
|
+
"rcfile": job.get("rcfile"),
|
|
241
|
+
"script": job.get("script"),
|
|
242
|
+
"command": job.get("command"),
|
|
243
|
+
"binary": binary,
|
|
244
|
+
"sharedir": sharedir,
|
|
245
|
+
"output_name": output_name,
|
|
246
|
+
"flat": bool(job.get("flat")),
|
|
247
|
+
"sim": bool(job.get("sim")),
|
|
248
|
+
"timeout_s": timeout,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
def execute(self, job):
|
|
252
|
+
"""Wrap the rcfile, netlist, and read back the output.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
job: Solver job with operation run_xschem plus
|
|
256
|
+
sch_file and deck inputs.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Native output with exit_code, stdout/stderr/log,
|
|
260
|
+
netlist file/text (raw and normalized), and flags.
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
AdapterError: invalid_reference for a missing
|
|
264
|
+
workdir, invalid_command for a bad command
|
|
265
|
+
snippet, missing_dependency on silent init
|
|
266
|
+
failure, runner_unavailable when no netlist
|
|
267
|
+
output appears, plus the preflight codes
|
|
268
|
+
(execute re-checks first).
|
|
269
|
+
"""
|
|
270
|
+
checked = self.preflight(job)
|
|
271
|
+
workdir = job.get("workdir", ".")
|
|
272
|
+
if not Path(workdir).is_dir():
|
|
273
|
+
raise AdapterError(
|
|
274
|
+
"invalid_reference", f"Workdir not found: {workdir}."
|
|
275
|
+
)
|
|
276
|
+
command = checked.get("command")
|
|
277
|
+
if command is not None and not isinstance(command, str):
|
|
278
|
+
raise AdapterError(
|
|
279
|
+
"invalid_command", f"Bad xschem command: {command!r}."
|
|
280
|
+
)
|
|
281
|
+
outdir = Path(workdir) / "xschem_out"
|
|
282
|
+
outdir.mkdir(exist_ok=True)
|
|
283
|
+
wrapper = Path(workdir) / "xschem_wrapper.rc"
|
|
284
|
+
lines = [f"set XSCHEM_SHAREDIR {{{checked['sharedir']}}}"]
|
|
285
|
+
if checked.get("rcfile") is not None:
|
|
286
|
+
lines.append(f"source {{{checked['rcfile']}}}")
|
|
287
|
+
lines.append(f"set XSCHEM_SHAREDIR {{{checked['sharedir']}}}")
|
|
288
|
+
wrapper.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
289
|
+
argv = [
|
|
290
|
+
checked["binary"],
|
|
291
|
+
"-x",
|
|
292
|
+
"-q",
|
|
293
|
+
"--rcfile",
|
|
294
|
+
str(wrapper),
|
|
295
|
+
"-s",
|
|
296
|
+
"-n",
|
|
297
|
+
checked["sch_file"],
|
|
298
|
+
"-o",
|
|
299
|
+
str(outdir),
|
|
300
|
+
"-N",
|
|
301
|
+
checked["output_name"],
|
|
302
|
+
]
|
|
303
|
+
if checked["flat"]:
|
|
304
|
+
argv.append("-f")
|
|
305
|
+
if checked["sim"]:
|
|
306
|
+
argv.append("-S")
|
|
307
|
+
if command:
|
|
308
|
+
argv.extend(["--command", command])
|
|
309
|
+
if checked.get("script") is not None:
|
|
310
|
+
argv.extend(["--script", checked["script"]])
|
|
311
|
+
log_file = Path(workdir) / "xschem.log"
|
|
312
|
+
proc = self._run(argv, workdir, timeout=checked["timeout_s"])
|
|
313
|
+
log_file.write_text(
|
|
314
|
+
(proc.stdout or "") + (proc.stderr or ""), encoding="utf-8"
|
|
315
|
+
)
|
|
316
|
+
log_text = log_file.read_text(encoding="utf-8")
|
|
317
|
+
if _INIT_ERR_RE.search(log_text):
|
|
318
|
+
raise AdapterError(
|
|
319
|
+
"missing_dependency",
|
|
320
|
+
"xschem init failed (xschem.tcl unloadable; sharedir "
|
|
321
|
+
f"{checked['sharedir']!r}); see xschem.log "
|
|
322
|
+
"(PROV: install xschem V3, see docs/tickets/PROV-DEFERRED.md).",
|
|
323
|
+
)
|
|
324
|
+
netlist_file = outdir / checked["output_name"]
|
|
325
|
+
if not netlist_file.is_file():
|
|
326
|
+
raise AdapterError(
|
|
327
|
+
"runner_unavailable",
|
|
328
|
+
f"xschem produced no netlist: {(proc.stderr or '')[:500]}.",
|
|
329
|
+
)
|
|
330
|
+
raw = netlist_file.read_text(encoding="utf-8")
|
|
331
|
+
return {
|
|
332
|
+
"exit_code": proc.returncode,
|
|
333
|
+
"stdout": proc.stdout,
|
|
334
|
+
"stderr": proc.stderr,
|
|
335
|
+
"log_file": str(log_file),
|
|
336
|
+
"log_text": log_text,
|
|
337
|
+
"netlist_file": str(netlist_file),
|
|
338
|
+
"netlist_text": raw,
|
|
339
|
+
"normalized_text": normalize_netlist(raw),
|
|
340
|
+
"flat": checked["flat"],
|
|
341
|
+
"sim": checked["sim"],
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
def parse(self, native_output):
|
|
345
|
+
"""Count netlist elements; sim mode surfaces sim failures.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
native_output: Native output with the netlist text
|
|
349
|
+
and log from execute.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
{total, violations[]} with element count and any
|
|
353
|
+
xschem/sim failures as violations.
|
|
354
|
+
|
|
355
|
+
Raises:
|
|
356
|
+
AdapterError: runner_unavailable when the output has
|
|
357
|
+
no netlist text.
|
|
358
|
+
"""
|
|
359
|
+
raw = native_output.get("netlist_text")
|
|
360
|
+
if raw is None:
|
|
361
|
+
netlist_file = native_output.get("netlist_file")
|
|
362
|
+
if not netlist_file or not Path(netlist_file).is_file():
|
|
363
|
+
raise AdapterError(
|
|
364
|
+
"runner_unavailable", "xschem run has no netlist."
|
|
365
|
+
)
|
|
366
|
+
raw = Path(netlist_file).read_text(encoding="utf-8")
|
|
367
|
+
elements = [
|
|
368
|
+
line
|
|
369
|
+
for line in raw.splitlines()
|
|
370
|
+
if line.strip() and not line.strip().startswith("*")
|
|
371
|
+
]
|
|
372
|
+
violations = []
|
|
373
|
+
if native_output.get("sim"):
|
|
374
|
+
log_text = native_output.get("log_text", "")
|
|
375
|
+
for line in log_text.splitlines():
|
|
376
|
+
if re.search(r"error|failed|no simulation", line, re.IGNORECASE):
|
|
377
|
+
violations.append(
|
|
378
|
+
{
|
|
379
|
+
"rule": "xschem_sim_fail",
|
|
380
|
+
"message": line.strip()[:300],
|
|
381
|
+
"severity": "error",
|
|
382
|
+
}
|
|
383
|
+
)
|
|
384
|
+
break
|
|
385
|
+
return {"total": len(elements), "violations": violations}
|
|
386
|
+
|
|
387
|
+
def _version_banner(self, binary, timeout):
|
|
388
|
+
"""Combined stdout/stderr of `xschem -v` for the V3 assert.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
binary: Resolved xschem binary path.
|
|
392
|
+
timeout: Budget seconds for the probe.
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
Banner text (empty when the probe cannot run — the
|
|
396
|
+
version assert then refuses).
|
|
397
|
+
"""
|
|
398
|
+
try:
|
|
399
|
+
proc = self._run([binary, "-v"], ".", timeout=timeout)
|
|
400
|
+
except AdapterError:
|
|
401
|
+
return ""
|
|
402
|
+
return (proc.stdout or "") + (proc.stderr or "")
|
|
403
|
+
|
|
404
|
+
def _run(self, argv, workdir, timeout=_TIMEOUT_S):
|
|
405
|
+
"""Run xschem with DISPLAY scrubbed (forced text mode).
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
argv: xschem argument vector.
|
|
409
|
+
workdir: Run directory.
|
|
410
|
+
timeout: Budget seconds for the run.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
Completed process with xschem stdout/stderr.
|
|
414
|
+
|
|
415
|
+
Raises:
|
|
416
|
+
AdapterError: runner_unavailable on timeout, plus
|
|
417
|
+
converted runner errors when injected.
|
|
418
|
+
"""
|
|
419
|
+
if self._runner is not None:
|
|
420
|
+
try:
|
|
421
|
+
return self._runner.run(argv, workdir, timeout)
|
|
422
|
+
except ExecutionError as exc:
|
|
423
|
+
raise AdapterError(exc.code, str(exc)) from exc
|
|
424
|
+
import os
|
|
425
|
+
|
|
426
|
+
env = dict(os.environ)
|
|
427
|
+
env.pop("DISPLAY", None)
|
|
428
|
+
try:
|
|
429
|
+
return subprocess.run(
|
|
430
|
+
argv,
|
|
431
|
+
capture_output=True,
|
|
432
|
+
text=True,
|
|
433
|
+
timeout=timeout,
|
|
434
|
+
cwd=workdir,
|
|
435
|
+
env=env,
|
|
436
|
+
)
|
|
437
|
+
except subprocess.TimeoutExpired as exc:
|
|
438
|
+
raise AdapterError(
|
|
439
|
+
"runner_unavailable", f"xschem timed out after {timeout}s."
|
|
440
|
+
) from exc
|