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,266 @@
|
|
|
1
|
+
"""Xschem netlist-to-sim deck: one call from schematic to verdict.
|
|
2
|
+
|
|
3
|
+
The solver adapter (`src/adapters/xschem/__init__.py`) netlists one
|
|
4
|
+
schematic per call. This deck chains the two deck phases behind one
|
|
5
|
+
call: netlist the schematic, refuse an empty (device-free) netlist
|
|
6
|
+
loudly instead of fake-greening it, and optionally run the
|
|
7
|
+
schematic-owned sim (`-S`) whose failures combine under the same
|
|
8
|
+
verdict. Engine vocabulary/composition wiring belongs to the owning
|
|
9
|
+
surface; this file only orchestrates the solver.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from ...interfaces import ToolAdapter
|
|
15
|
+
from ..errors import AdapterError
|
|
16
|
+
from . import XschemAdapter
|
|
17
|
+
|
|
18
|
+
OPERATION = "run_xschem"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def deck_elements(text):
|
|
22
|
+
"""Device lines of one netlist, without comments or directives.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
text: Raw (or normalized) netlist text.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Device lines such as `V1 ...` or `X...`, in file order.
|
|
29
|
+
Comment lines (`*...`), dot directives (`.end`,
|
|
30
|
+
`.GLOBAL`), and blanks never count as devices.
|
|
31
|
+
"""
|
|
32
|
+
lines = []
|
|
33
|
+
for line in (text or "").splitlines():
|
|
34
|
+
stripped = line.strip()
|
|
35
|
+
if not stripped:
|
|
36
|
+
continue
|
|
37
|
+
if stripped.startswith(("*", ".")):
|
|
38
|
+
continue
|
|
39
|
+
lines.append(line)
|
|
40
|
+
return lines
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class XschemDeck(ToolAdapter):
|
|
44
|
+
"""run_xschem as a deck: netlist, guard empty, optionally sim.
|
|
45
|
+
|
|
46
|
+
Serves the run_xschem operation with tool `xschem` in deck
|
|
47
|
+
modes (`netlist`, `sim`, `deck`). Phase one netlists the
|
|
48
|
+
schematic; a netlist with zero device lines fails loudly as
|
|
49
|
+
`empty_netlist` (an empty schematic or all symbols missing
|
|
50
|
+
never parses as clean). Phase two, only when the job sets
|
|
51
|
+
`sim`, reruns with the schematic-owned sim (`-S`) and reports
|
|
52
|
+
its failures under `sim`.
|
|
53
|
+
|
|
54
|
+
Job keys:
|
|
55
|
+
sch_file (required): Schematic file under netlist.
|
|
56
|
+
rcfile: Project rcfile path (library paths may derive
|
|
57
|
+
from the adapter-pinned XSCHEM_SHAREDIR).
|
|
58
|
+
output_name: Netlist file name (default `<stem>.spice`).
|
|
59
|
+
flat: Flat netlist (`-f`) when true.
|
|
60
|
+
sim: Run the schematic-owned sim (`-S`) when true.
|
|
61
|
+
workdir: Run directory (default ".").
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
runner: Optional isolated runner; direct subprocess when None.
|
|
65
|
+
xschem_bin: Explicit xschem binary; PATH when None.
|
|
66
|
+
|
|
67
|
+
Caution: The deck reruns the netlister for the sim phase, so
|
|
68
|
+
a schematic whose outputs differ run to run decks
|
|
69
|
+
non-deterministically — byte-compare only the netlist
|
|
70
|
+
phase, never the sim phase.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, runner=None, xschem_bin=None):
|
|
74
|
+
self._runner = runner
|
|
75
|
+
self._xschem_bin = xschem_bin
|
|
76
|
+
|
|
77
|
+
def capabilities(self, target):
|
|
78
|
+
"""Report the served operation, tool, and deck modes.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
target: Target the capabilities are reported for
|
|
82
|
+
(accepted, unused — one schematic per job).
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Capability mapping with operations, tool, and modes.
|
|
86
|
+
"""
|
|
87
|
+
return {
|
|
88
|
+
"operations": [OPERATION],
|
|
89
|
+
"tool": "xschem",
|
|
90
|
+
"modes": ["netlist", "sim", "deck"],
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def _adapter(self):
|
|
94
|
+
"""One solver adapter sharing this deck's runner and binary."""
|
|
95
|
+
return XschemAdapter(
|
|
96
|
+
runner=self._runner, xschem_bin=self._xschem_bin)
|
|
97
|
+
|
|
98
|
+
def netlist(self, job):
|
|
99
|
+
"""Netlist one schematic and parse it to a deck verdict.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
job: Solver job with operation run_xschem plus
|
|
103
|
+
sch_file and deck inputs (any `sim` flag is
|
|
104
|
+
ignored: this phase never simulates).
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Mapping with netlist_file, normalized_text, elements
|
|
108
|
+
(device lines), total, and violations.
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
AdapterError: invalid_reference for a missing
|
|
112
|
+
schematic/rcfile, missing_dependency without
|
|
113
|
+
xschem V3, runner_unavailable with no netlist
|
|
114
|
+
output, plus the solver preflight codes.
|
|
115
|
+
"""
|
|
116
|
+
adapter = self._adapter()
|
|
117
|
+
native = adapter.execute(dict(job, sim=False))
|
|
118
|
+
parsed = adapter.parse(native)
|
|
119
|
+
elements = deck_elements(native.get("normalized_text"))
|
|
120
|
+
return {
|
|
121
|
+
"netlist_file": native.get("netlist_file"),
|
|
122
|
+
"normalized_text": native.get("normalized_text"),
|
|
123
|
+
"elements": elements,
|
|
124
|
+
"total": parsed.get("total"),
|
|
125
|
+
"violations": list(parsed.get("violations") or []),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
def deck(self, job):
|
|
129
|
+
"""Deck one schematic from netlist to an optional sim verdict.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
job: Solver job with operation run_xschem plus
|
|
133
|
+
sch_file and deck inputs; `sim` true adds the
|
|
134
|
+
schematic-owned sim phase.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Netlist verdict (netlist_file, normalized_text,
|
|
138
|
+
elements, total, violations) plus `sim`: None
|
|
139
|
+
without the sim flag, else the sim failures as
|
|
140
|
+
{total, violations, log_file}. A device-free
|
|
141
|
+
netlist appends an `empty_netlist` error and
|
|
142
|
+
skips the sim phase.
|
|
143
|
+
|
|
144
|
+
Raises:
|
|
145
|
+
AdapterError: invalid_reference for a missing
|
|
146
|
+
schematic, missing_dependency without xschem
|
|
147
|
+
V3, runner_unavailable with no netlist output,
|
|
148
|
+
plus the solver preflight codes (netlist first,
|
|
149
|
+
sim reruns the same checks).
|
|
150
|
+
"""
|
|
151
|
+
verdict = self.netlist(job)
|
|
152
|
+
if not verdict["elements"]:
|
|
153
|
+
verdict["violations"] = verdict["violations"] + [{
|
|
154
|
+
"rule": "empty_netlist",
|
|
155
|
+
"severity": "error",
|
|
156
|
+
"message": ("xschem deck netlist has no device lines: "
|
|
157
|
+
"empty schematic or all symbols missing."),
|
|
158
|
+
}]
|
|
159
|
+
verdict["total"] = len(verdict["violations"])
|
|
160
|
+
verdict["sim"] = None
|
|
161
|
+
return verdict
|
|
162
|
+
if not job.get("sim"):
|
|
163
|
+
verdict["sim"] = None
|
|
164
|
+
return verdict
|
|
165
|
+
adapter = self._adapter()
|
|
166
|
+
sim_native = adapter.execute(dict(job, sim=True))
|
|
167
|
+
sim_parsed = adapter.parse(sim_native)
|
|
168
|
+
sim_fails = [v for v in (sim_parsed.get("violations") or [])
|
|
169
|
+
if v.get("rule") == "xschem_sim_fail"]
|
|
170
|
+
verdict["sim"] = {
|
|
171
|
+
"total": len(sim_fails),
|
|
172
|
+
"violations": sim_fails,
|
|
173
|
+
"log_file": sim_native.get("log_file"),
|
|
174
|
+
}
|
|
175
|
+
return verdict
|
|
176
|
+
|
|
177
|
+
def preflight(self, job):
|
|
178
|
+
"""Check the deck job through the solver preflight.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
job: Solver job with operation run_xschem plus
|
|
182
|
+
sch_file and deck inputs.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Checked mapping from the solver preflight.
|
|
186
|
+
|
|
187
|
+
Raises:
|
|
188
|
+
AdapterError: unsupported_operation, invalid_reference,
|
|
189
|
+
invalid_command, or missing_dependency, exactly as
|
|
190
|
+
the solver reports them.
|
|
191
|
+
"""
|
|
192
|
+
return self._adapter().preflight(job)
|
|
193
|
+
|
|
194
|
+
def execute(self, job):
|
|
195
|
+
"""Deck one schematic: netlist natively, sim natively on flag.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
job: Solver job with operation run_xschem plus
|
|
199
|
+
sch_file and deck inputs.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Native output with the netlist phase and, on the sim
|
|
203
|
+
flag with devices present, the sim phase.
|
|
204
|
+
|
|
205
|
+
Raises:
|
|
206
|
+
AdapterError: invalid_reference, missing_dependency,
|
|
207
|
+
or runner_unavailable, exactly as the solver
|
|
208
|
+
reports them.
|
|
209
|
+
"""
|
|
210
|
+
net_native = self._adapter().execute(dict(job, sim=False))
|
|
211
|
+
phases = {"netlist": net_native, "sim": None}
|
|
212
|
+
if job.get("sim") and deck_elements(
|
|
213
|
+
net_native.get("normalized_text")):
|
|
214
|
+
phases["sim"] = self._adapter().execute(dict(job, sim=True))
|
|
215
|
+
return phases
|
|
216
|
+
|
|
217
|
+
def parse(self, native_output):
|
|
218
|
+
"""Parse deck natives to the combined deck verdict.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
native_output: Native output with netlist (and
|
|
222
|
+
optional sim) phases from execute.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Deck verdict with elements, violations (including
|
|
226
|
+
the empty_netlist guard), and the sim sub-verdict.
|
|
227
|
+
|
|
228
|
+
Raises:
|
|
229
|
+
AdapterError: runner_unavailable when the netlist
|
|
230
|
+
phase is missing.
|
|
231
|
+
"""
|
|
232
|
+
adapter = self._adapter()
|
|
233
|
+
net_native = (native_output or {}).get("netlist")
|
|
234
|
+
if net_native is None:
|
|
235
|
+
raise AdapterError(
|
|
236
|
+
"runner_unavailable", "xschem deck has no netlist phase.")
|
|
237
|
+
parsed = adapter.parse(net_native)
|
|
238
|
+
elements = deck_elements(net_native.get("normalized_text"))
|
|
239
|
+
violations = list(parsed.get("violations") or [])
|
|
240
|
+
if not elements:
|
|
241
|
+
violations = violations + [{
|
|
242
|
+
"rule": "empty_netlist",
|
|
243
|
+
"severity": "error",
|
|
244
|
+
"message": ("xschem deck netlist has no device lines: "
|
|
245
|
+
"empty schematic or all symbols missing."),
|
|
246
|
+
}]
|
|
247
|
+
sim_native = (native_output or {}).get("sim")
|
|
248
|
+
sim = None
|
|
249
|
+
if sim_native is not None:
|
|
250
|
+
sim_parsed = adapter.parse(sim_native)
|
|
251
|
+
sim_fails = [v for v in (sim_parsed.get("violations") or [])
|
|
252
|
+
if v.get("rule") == "xschem_sim_fail"]
|
|
253
|
+
sim = {
|
|
254
|
+
"total": len(sim_fails),
|
|
255
|
+
"violations": sim_fails,
|
|
256
|
+
"log_file": sim_native.get("log_file"),
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
"netlist_file": net_native.get("netlist_file"),
|
|
260
|
+
"normalized_text": net_native.get("normalized_text"),
|
|
261
|
+
"elements": elements,
|
|
262
|
+
"total": (len(violations) if not elements
|
|
263
|
+
else parsed.get("total")),
|
|
264
|
+
"violations": violations,
|
|
265
|
+
"sim": sim,
|
|
266
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""Shared client over one engine's integrated runtime (CLIENTS).
|
|
2
|
+
|
|
3
|
+
Every transport (MCP, HTTP, IPC, stdio console, Python) serves the
|
|
4
|
+
same EngineRuntime through this one client instead of inventing
|
|
5
|
+
its own queue. All run control delegates to the engine's public
|
|
6
|
+
deferred surface — the client never constructs a second runtime,
|
|
7
|
+
never reimplements admission, and never synthesizes verdicts.
|
|
8
|
+
|
|
9
|
+
Caution: The push-listener handle (`add_listener`) stays behind
|
|
10
|
+
the runtime object the servers bind explicitly; the engine
|
|
11
|
+
exposes no public listener accessor yet (see the API-V1-CLIENTS
|
|
12
|
+
verdict carry). Watch polling and cursor recovery ride the
|
|
13
|
+
public `runtime_events` surface instead.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from ..project.errors import ProjectError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RuntimeClient:
|
|
20
|
+
"""One engine's deferred runs, served to every client alike.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
engine: Engine whose integrated runtime this client
|
|
24
|
+
serves (submit, deferred submit/pump/wait/fetch,
|
|
25
|
+
cancel, watch, survivors).
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, engine):
|
|
29
|
+
self._engine = engine
|
|
30
|
+
self._paused = False
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def engine(self):
|
|
34
|
+
"""Engine this client serves.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The bound engine.
|
|
38
|
+
"""
|
|
39
|
+
return self._engine
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def paused(self):
|
|
43
|
+
"""Client-side capacity signal from the last refusal.
|
|
44
|
+
|
|
45
|
+
True after a deferred admission refused with admitted
|
|
46
|
+
False (retryable saturation); False again once a pump,
|
|
47
|
+
cancel, or admitted submit frees the lane.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Whether the client believes admission is paused.
|
|
51
|
+
"""
|
|
52
|
+
return self._paused
|
|
53
|
+
|
|
54
|
+
def submit(self, project_id, operation, arguments, request_id=None):
|
|
55
|
+
"""Submit one call inline through the engine.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
project_id: Project the call runs against.
|
|
59
|
+
operation: Cataloged operation or query name.
|
|
60
|
+
arguments: Operation arguments mapping.
|
|
61
|
+
request_id: Client envelope id; minted when omitted.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Engine result for the call (ack, completion, or data).
|
|
65
|
+
"""
|
|
66
|
+
command = {
|
|
67
|
+
"project_id": project_id,
|
|
68
|
+
"operation": operation,
|
|
69
|
+
"arguments": dict(arguments),
|
|
70
|
+
}
|
|
71
|
+
if request_id is not None:
|
|
72
|
+
command["request_id"] = request_id
|
|
73
|
+
return self._engine.submit(command)
|
|
74
|
+
|
|
75
|
+
def deferred_submit(self, operation, arguments, request_id=None):
|
|
76
|
+
"""Admit one deferred run with a prompt acknowledgement.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
operation: Cataloged operation name.
|
|
80
|
+
arguments: Operation arguments mapping.
|
|
81
|
+
request_id: Client envelope id; minted when omitted.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Deferred handle watching the admitted run; the
|
|
85
|
+
receipt carries no verdict. A refused handle carries
|
|
86
|
+
admitted False and marks this client paused.
|
|
87
|
+
"""
|
|
88
|
+
handle = self._engine.deferred_submit(
|
|
89
|
+
operation, dict(arguments or {}), request_id=request_id)
|
|
90
|
+
receipt = handle.receipt
|
|
91
|
+
if receipt.get("admitted") is False:
|
|
92
|
+
self._paused = True
|
|
93
|
+
else:
|
|
94
|
+
self._paused = False
|
|
95
|
+
return handle
|
|
96
|
+
|
|
97
|
+
def pump(self, limit=None):
|
|
98
|
+
"""Dispatch queued deferred runs oldest first.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
limit: Maximum entries to drain; the whole backlog
|
|
102
|
+
when omitted.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Completions in dispatch order. A drained lane clears
|
|
106
|
+
this client's paused signal.
|
|
107
|
+
"""
|
|
108
|
+
completions = self._engine.deferred_pump(limit=limit)
|
|
109
|
+
if completions:
|
|
110
|
+
self._paused = False
|
|
111
|
+
return completions
|
|
112
|
+
|
|
113
|
+
def wait_for(self, run_id, timeout=None):
|
|
114
|
+
"""Block until one deferred run reaches a terminal state.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
run_id: Deferred run id to wait for.
|
|
118
|
+
timeout: Maximum seconds to wait; waits with the run
|
|
119
|
+
when omitted.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Terminal completion for the run.
|
|
123
|
+
"""
|
|
124
|
+
return self._engine.deferred_wait(run_id, timeout=timeout)
|
|
125
|
+
|
|
126
|
+
def status(self, run_id):
|
|
127
|
+
"""Read one deferred run's state, verdict, and operation.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
run_id: Deferred run id to inspect.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Status with run_id, operation, state, and verdict
|
|
134
|
+
once judged.
|
|
135
|
+
"""
|
|
136
|
+
return self._engine.deferred_status(run_id)
|
|
137
|
+
|
|
138
|
+
def fetch(self, run_id):
|
|
139
|
+
"""Fetch one deferred run's merged ack-plus-completion.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
run_id: Deferred run id to fetch.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Merged receipt-plus-completion; verdict present once
|
|
146
|
+
the run finished.
|
|
147
|
+
"""
|
|
148
|
+
return self._engine.deferred_fetch(run_id)
|
|
149
|
+
|
|
150
|
+
def cancel(self, run_id, actor=None):
|
|
151
|
+
"""Cancel one queued or running deferred run.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
run_id: Deferred run id to cancel.
|
|
155
|
+
actor: Attribution for the lifecycle event; the
|
|
156
|
+
engine actor when omitted.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Outcome with run_id, state, actor, and verdict when
|
|
160
|
+
judged. A freed lane clears this client's paused
|
|
161
|
+
signal.
|
|
162
|
+
"""
|
|
163
|
+
outcome = self._engine.runtime_cancel(run_id, actor=actor)
|
|
164
|
+
self._paused = False
|
|
165
|
+
return outcome
|
|
166
|
+
|
|
167
|
+
def watch(self, cursor=0):
|
|
168
|
+
"""Read ordered lifecycle events after a cursor.
|
|
169
|
+
|
|
170
|
+
A cursor the retained log no longer covers recovers
|
|
171
|
+
loudly: the page replays from the floor and reports
|
|
172
|
+
resynced True with the current revision instead of
|
|
173
|
+
failing the subscriber.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
cursor: Opaque position from a previous read; 0
|
|
177
|
+
replays the retained log.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Page with events oldest first, the current cursor,
|
|
181
|
+
and resynced True only after a recovery replay.
|
|
182
|
+
|
|
183
|
+
Raises:
|
|
184
|
+
ProjectError: any runtime refusal other than
|
|
185
|
+
cursor_expired — expired cursors recover,
|
|
186
|
+
never raise.
|
|
187
|
+
"""
|
|
188
|
+
try:
|
|
189
|
+
page = self._engine.runtime_events(cursor)
|
|
190
|
+
except ProjectError as err:
|
|
191
|
+
if err.code != "cursor_expired":
|
|
192
|
+
raise
|
|
193
|
+
snapshot = self._engine.snapshot()
|
|
194
|
+
page = self._engine.runtime_events(0)
|
|
195
|
+
return {
|
|
196
|
+
"events": page["events"],
|
|
197
|
+
"cursor": page["cursor"],
|
|
198
|
+
"resynced": True,
|
|
199
|
+
"current_revision": snapshot["current_revision"],
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
"events": page["events"],
|
|
203
|
+
"cursor": page["cursor"],
|
|
204
|
+
"resynced": False,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
def survivors(self):
|
|
208
|
+
"""Report unfinished deferred runs without executing any.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Boot report with survivors, interrupted, and
|
|
212
|
+
quarantined run ids.
|
|
213
|
+
"""
|
|
214
|
+
return self._engine.boot_report()
|
|
215
|
+
|
|
216
|
+
def resume(self, run_id, force=False):
|
|
217
|
+
"""Resume one crash-surviving deferred run.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
run_id: Survivor to execute now.
|
|
221
|
+
force: Required to resume a quarantined run.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Resume outcome for the run.
|
|
225
|
+
"""
|
|
226
|
+
return self._engine.resume_survivor(run_id, force=force)
|
|
227
|
+
|
|
228
|
+
def discard(self, run_id):
|
|
229
|
+
"""Discard one crash-surviving deferred run.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
run_id: Survivor to discard.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
Discard record for the run.
|
|
236
|
+
"""
|
|
237
|
+
return self._engine.discard_survivor(run_id)
|
|
238
|
+
|
|
239
|
+
def resume_all(self):
|
|
240
|
+
"""Resume every retryable crash survivor.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Gated batch with resumed run ids, their
|
|
244
|
+
completions, and skipped entries.
|
|
245
|
+
"""
|
|
246
|
+
return self._engine.resume_all_retryable()
|
|
247
|
+
|
|
248
|
+
def replay_pending(self):
|
|
249
|
+
"""Resume crash-surviving RETRYABLE admissions, oldest first.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Report with replayed run ids, their completions, and
|
|
253
|
+
skipped entries.
|
|
254
|
+
"""
|
|
255
|
+
return self._engine.replay_pending()
|