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.
Files changed (241) hide show
  1. rocket_engine/__init__.py +8 -0
  2. rocket_engine/__main__.py +12 -0
  3. rocket_engine/adapters/README.md +54 -0
  4. rocket_engine/adapters/__init__.py +7 -0
  5. rocket_engine/adapters/api.py +39 -0
  6. rocket_engine/adapters/base.py +46 -0
  7. rocket_engine/adapters/cace/__init__.py +163 -0
  8. rocket_engine/adapters/circulax/README.md +10 -0
  9. rocket_engine/adapters/circulax/__init__.py +112 -0
  10. rocket_engine/adapters/circulax/_driver.py +190 -0
  11. rocket_engine/adapters/circulax/adapter.py +538 -0
  12. rocket_engine/adapters/circulax/results.py +291 -0
  13. rocket_engine/adapters/cli/README.md +34 -0
  14. rocket_engine/adapters/cli/__init__.py +305 -0
  15. rocket_engine/adapters/connectivity/__init__.py +196 -0
  16. rocket_engine/adapters/connectivity/results.py +214 -0
  17. rocket_engine/adapters/cosim/README.md +9 -0
  18. rocket_engine/adapters/cosim/__init__.py +1390 -0
  19. rocket_engine/adapters/cosim/results.py +214 -0
  20. rocket_engine/adapters/devsim/__init__.py +171 -0
  21. rocket_engine/adapters/digital_flow/README.md +9 -0
  22. rocket_engine/adapters/digital_flow/__init__.py +1121 -0
  23. rocket_engine/adapters/digital_flow/results.py +227 -0
  24. rocket_engine/adapters/digital_flow/state.py +462 -0
  25. rocket_engine/adapters/drc/README.klayout.md +7 -0
  26. rocket_engine/adapters/drc/README.md +7 -0
  27. rocket_engine/adapters/drc/__init__.py +1 -0
  28. rocket_engine/adapters/drc/klayout.py +952 -0
  29. rocket_engine/adapters/drc/magic.py +287 -0
  30. rocket_engine/adapters/drc/results.py +184 -0
  31. rocket_engine/adapters/drc/results_kl.py +188 -0
  32. rocket_engine/adapters/elmer/__init__.py +163 -0
  33. rocket_engine/adapters/envelope.py +120 -0
  34. rocket_engine/adapters/equivalence/__init__.py +156 -0
  35. rocket_engine/adapters/errors.py +181 -0
  36. rocket_engine/adapters/extract_magic/README.md +15 -0
  37. rocket_engine/adapters/extract_magic/__init__.py +425 -0
  38. rocket_engine/adapters/extract_magic/results.py +209 -0
  39. rocket_engine/adapters/formal_sby/README.md +7 -0
  40. rocket_engine/adapters/formal_sby/__init__.py +773 -0
  41. rocket_engine/adapters/formal_sby/formal_edge.py +195 -0
  42. rocket_engine/adapters/formal_sby/results.py +185 -0
  43. rocket_engine/adapters/gdsfactory/__init__.py +194 -0
  44. rocket_engine/adapters/gdsfactory/_driver.py +459 -0
  45. rocket_engine/adapters/gdsfactory/adapter.py +609 -0
  46. rocket_engine/adapters/gdsfactory/results.py +181 -0
  47. rocket_engine/adapters/hdl_sim/README.md +7 -0
  48. rocket_engine/adapters/hdl_sim/__init__.py +369 -0
  49. rocket_engine/adapters/hdl_sim/ghdl.py +155 -0
  50. rocket_engine/adapters/hdl_sim/results.py +178 -0
  51. rocket_engine/adapters/hdl_sim/verilator.py +167 -0
  52. rocket_engine/adapters/http/README.md +15 -0
  53. rocket_engine/adapters/http/__init__.py +458 -0
  54. rocket_engine/adapters/http/events.py +63 -0
  55. rocket_engine/adapters/ipc/README.md +9 -0
  56. rocket_engine/adapters/ipc/__init__.py +111 -0
  57. rocket_engine/adapters/lvs_netgen/README.md +20 -0
  58. rocket_engine/adapters/lvs_netgen/__init__.py +889 -0
  59. rocket_engine/adapters/lvs_netgen/results.py +168 -0
  60. rocket_engine/adapters/mcp/README.md +19 -0
  61. rocket_engine/adapters/mcp/__init__.py +85 -0
  62. rocket_engine/adapters/mcp/api-schema.json +6107 -0
  63. rocket_engine/adapters/mcp/pull.py +246 -0
  64. rocket_engine/adapters/mcp/server.py +794 -0
  65. rocket_engine/adapters/meep/__init__.py +167 -0
  66. rocket_engine/adapters/meow/README.md +7 -0
  67. rocket_engine/adapters/meow/__init__.py +190 -0
  68. rocket_engine/adapters/meow/_driver.py +227 -0
  69. rocket_engine/adapters/meow/_envelope.py +108 -0
  70. rocket_engine/adapters/meow/adapter.py +491 -0
  71. rocket_engine/adapters/meow/results.py +218 -0
  72. rocket_engine/adapters/mutation/__init__.py +192 -0
  73. rocket_engine/adapters/ngspice/README.md +28 -0
  74. rocket_engine/adapters/ngspice/__init__.py +627 -0
  75. rocket_engine/adapters/ngspice/results.py +215 -0
  76. rocket_engine/adapters/postlayout/README.md +28 -0
  77. rocket_engine/adapters/postlayout/__init__.py +17 -0
  78. rocket_engine/adapters/postlayout/adapter.py +1062 -0
  79. rocket_engine/adapters/postlayout/results.py +330 -0
  80. rocket_engine/adapters/postlayout/translate.py +71 -0
  81. rocket_engine/adapters/profile.py +220 -0
  82. rocket_engine/adapters/sax/README.md +23 -0
  83. rocket_engine/adapters/sax/__init__.py +516 -0
  84. rocket_engine/adapters/sax/_driver.py +148 -0
  85. rocket_engine/adapters/sax/results.py +291 -0
  86. rocket_engine/adapters/stdio_console.py +1151 -0
  87. rocket_engine/adapters/syntax/README.md +5 -0
  88. rocket_engine/adapters/syntax/__init__.py +366 -0
  89. rocket_engine/adapters/syntax/results.py +181 -0
  90. rocket_engine/adapters/synth_yosys/README.md +7 -0
  91. rocket_engine/adapters/synth_yosys/__init__.py +655 -0
  92. rocket_engine/adapters/synth_yosys/amaranth.py +120 -0
  93. rocket_engine/adapters/synth_yosys/ghdl.py +128 -0
  94. rocket_engine/adapters/synth_yosys/results.py +173 -0
  95. rocket_engine/adapters/timing_sta/README.md +9 -0
  96. rocket_engine/adapters/timing_sta/__init__.py +726 -0
  97. rocket_engine/adapters/view_sync/__init__.py +185 -0
  98. rocket_engine/adapters/view_sync/results.py +189 -0
  99. rocket_engine/adapters/vlsir/__init__.py +142 -0
  100. rocket_engine/adapters/xschem/__init__.py +440 -0
  101. rocket_engine/adapters/xschem/deck.py +266 -0
  102. rocket_engine/clients/__init__.py +5 -0
  103. rocket_engine/clients/runtime.py +255 -0
  104. rocket_engine/composition.py +2279 -0
  105. rocket_engine/execution/README.md +30 -0
  106. rocket_engine/execution/__init__.py +150 -0
  107. rocket_engine/execution/apig/__init__.py +1 -0
  108. rocket_engine/execution/apig/pool.py +463 -0
  109. rocket_engine/execution/apig/submit.py +124 -0
  110. rocket_engine/execution/block_runs_scope.py +479 -0
  111. rocket_engine/execution/coalesce.py +414 -0
  112. rocket_engine/execution/console/__init__.py +1 -0
  113. rocket_engine/execution/console/apply_session.py +318 -0
  114. rocket_engine/execution/console/lifecycle.py +265 -0
  115. rocket_engine/execution/dispatch.py +205 -0
  116. rocket_engine/execution/errors.py +31 -0
  117. rocket_engine/execution/ports.py +797 -0
  118. rocket_engine/execution/pyexec/__init__.py +1 -0
  119. rocket_engine/execution/pyexec/apply_context.py +547 -0
  120. rocket_engine/execution/pyexec/approvals.py +173 -0
  121. rocket_engine/execution/pyexec/runner.py +404 -0
  122. rocket_engine/execution/pyexec/support.py +464 -0
  123. rocket_engine/execution/python_exec.py +897 -0
  124. rocket_engine/execution/queue.py +85 -0
  125. rocket_engine/execution/replay_safe.py +601 -0
  126. rocket_engine/execution/routing/__init__.py +1 -0
  127. rocket_engine/execution/routing/calls.py +364 -0
  128. rocket_engine/execution/routing/namespace.py +785 -0
  129. rocket_engine/execution/routing/ports.py +102 -0
  130. rocket_engine/execution/routing/tools.py +346 -0
  131. rocket_engine/execution/runner.py +311 -0
  132. rocket_engine/execution/runtime.py +1149 -0
  133. rocket_engine/execution/service.py +279 -0
  134. rocket_engine/execution/worker.py +464 -0
  135. rocket_engine/interfaces/README.md +26 -0
  136. rocket_engine/interfaces/__init__.py +58 -0
  137. rocket_engine/interfaces/apig_assets.py +407 -0
  138. rocket_engine/interfaces/celllink.py +461 -0
  139. rocket_engine/interfaces/documents.py +92 -0
  140. rocket_engine/interfaces/envelopes.py +131 -0
  141. rocket_engine/interfaces/errors.py +16 -0
  142. rocket_engine/interfaces/layout.py +410 -0
  143. rocket_engine/interfaces/layout_schema.yaml +278 -0
  144. rocket_engine/interfaces/paths.py +223 -0
  145. rocket_engine/interfaces/pdks.py +550 -0
  146. rocket_engine/interfaces/ports.py +72 -0
  147. rocket_engine/interfaces/references.py +115 -0
  148. rocket_engine/interfaces/refs.py +85 -0
  149. rocket_engine/interfaces/runparams.py +304 -0
  150. rocket_engine/interfaces/setup.py +552 -0
  151. rocket_engine/interfaces/setup_catalogue.yaml +243 -0
  152. rocket_engine/interfaces/setup_probes.py +66 -0
  153. rocket_engine/interfaces/vocabulary.py +58 -0
  154. rocket_engine/interfaces/yaml_dump.py +31 -0
  155. rocket_engine/library/README.md +22 -0
  156. rocket_engine/library/__init__.py +13 -0
  157. rocket_engine/library/apig_catalog.py +371 -0
  158. rocket_engine/library/catalog.py +185 -0
  159. rocket_engine/library/errors.py +16 -0
  160. rocket_engine/mutate/__init__.py +776 -0
  161. rocket_engine/mutate/operators.py +700 -0
  162. rocket_engine/ops/__init__.py +1 -0
  163. rocket_engine/ops/deferred.py +279 -0
  164. rocket_engine/ops/editors.py +404 -0
  165. rocket_engine/ops/evidence.py +194 -0
  166. rocket_engine/ops/gates.py +262 -0
  167. rocket_engine/ops/ports.py +76 -0
  168. rocket_engine/ops/queries.py +417 -0
  169. rocket_engine/ops/runs.py +553 -0
  170. rocket_engine/ops/submit.py +614 -0
  171. rocket_engine/project/README.md +27 -0
  172. rocket_engine/project/__init__.py +6 -0
  173. rocket_engine/project/canvas/__init__.py +55 -0
  174. rocket_engine/project/canvas/compile.py +109 -0
  175. rocket_engine/project/canvas/integrate.py +48 -0
  176. rocket_engine/project/canvas/schema.py +241 -0
  177. rocket_engine/project/canvas/templates.py +46 -0
  178. rocket_engine/project/canvas/validate.py +603 -0
  179. rocket_engine/project/edit/__init__.py +5 -0
  180. rocket_engine/project/edit/cas.py +87 -0
  181. rocket_engine/project/edit/transactions.py +282 -0
  182. rocket_engine/project/errors.py +16 -0
  183. rocket_engine/project/frontend13.py +685 -0
  184. rocket_engine/project/geometry/__init__.py +23 -0
  185. rocket_engine/project/geometry/ops.py +451 -0
  186. rocket_engine/project/geometry/snap.py +103 -0
  187. rocket_engine/project/ip/__init__.py +1 -0
  188. rocket_engine/project/ip/blocks.py +195 -0
  189. rocket_engine/project/ip/entry.schema.json +78 -0
  190. rocket_engine/project/ip/lifecycle.py +402 -0
  191. rocket_engine/project/ip/verbs.py +123 -0
  192. rocket_engine/project/layers/__init__.py +6 -0
  193. rocket_engine/project/layers/bindings.py +115 -0
  194. rocket_engine/project/layers/catalog.py +228 -0
  195. rocket_engine/project/layers/presets.py +43 -0
  196. rocket_engine/project/ledger/__init__.py +1 -0
  197. rocket_engine/project/ledger/docs.py +167 -0
  198. rocket_engine/project/ledger/entities.py +176 -0
  199. rocket_engine/project/ledger/events.py +29 -0
  200. rocket_engine/project/ledger/runs.py +257 -0
  201. rocket_engine/project/ledger/shared.py +174 -0
  202. rocket_engine/project/ledger/snapshots.py +385 -0
  203. rocket_engine/project/ledger/wires.py +552 -0
  204. rocket_engine/project/process/__init__.py +14 -0
  205. rocket_engine/project/process/model.py +193 -0
  206. rocket_engine/project/process/recipes.py +121 -0
  207. rocket_engine/project/schematic/__init__.py +1 -0
  208. rocket_engine/project/schematic/hierarchy.py +312 -0
  209. rocket_engine/project/schematic/instances.py +393 -0
  210. rocket_engine/project/schematic/sheets.py +198 -0
  211. rocket_engine/project/service.py +2941 -0
  212. rocket_engine/project/symbols/__init__.py +1 -0
  213. rocket_engine/project/symbols/cells.py +324 -0
  214. rocket_engine/project/symbols/linkage.py +356 -0
  215. rocket_engine/project/symbols/symbols.py +352 -0
  216. rocket_engine/project/templates/__init__.py +303 -0
  217. rocket_engine/project/templates/errors.py +25 -0
  218. rocket_engine/project/testbench/__init__.py +49 -0
  219. rocket_engine/project/testbench/blocks.py +619 -0
  220. rocket_engine/project/testbench/templates.py +134 -0
  221. rocket_engine/stores/README.md +12 -0
  222. rocket_engine/stores/__init__.py +6 -0
  223. rocket_engine/stores/ecs.py +1508 -0
  224. rocket_engine/stores/layers.py +1023 -0
  225. rocket_engine/stores/settings.py +1188 -0
  226. rocket_engine/stores/wireplace.py +1324 -0
  227. rocket_engine/workflows/README.md +18 -0
  228. rocket_engine/workflows/__init__.py +53 -0
  229. rocket_engine/workflows/capability_plan.py +102 -0
  230. rocket_engine/workflows/completion.py +228 -0
  231. rocket_engine/workflows/connectgraph.py +734 -0
  232. rocket_engine/workflows/connectgraph_emit.py +336 -0
  233. rocket_engine/workflows/gates.py +144 -0
  234. rocket_engine/workflows/photonic_wire.py +76 -0
  235. rocket_engine/workflows/stages.py +324 -0
  236. rocket_engine/workflows/viewsync.py +399 -0
  237. rocket_engine-0.0.2b1.dist-info/METADATA +200 -0
  238. rocket_engine-0.0.2b1.dist-info/RECORD +241 -0
  239. rocket_engine-0.0.2b1.dist-info/WHEEL +4 -0
  240. rocket_engine-0.0.2b1.dist-info/entry_points.txt +2 -0
  241. rocket_engine-0.0.2b1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,336 @@
1
+ """Raw-IR netlist emitters (CONNECT-GRAPH): SPICE, Verilog, VHDL.
2
+
3
+ `to_raw_ir` resolves one language-neutral record from extracted
4
+ structs — pins to canonical net names through `Connectivity.net_name`
5
+ plus the 4-level format chain — and each printer only arranges raw
6
+ lines and runs its small textual post-pass. Emitters never walk the
7
+ graph: they accept raw-IR mappings only. Bus expansion and line
8
+ joinery each live in exactly one shared helper.
9
+
10
+ Format chain, first non-empty template wins:
11
+
12
+ - instance_custom: instance `custom[language]`.
13
+ - symbol_custom: symbol `custom[language]`.
14
+ - instance_format: instance `format[language]`.
15
+ - symbol_format: symbol `format[language]`.
16
+
17
+ Templates substitute {name}, {symbol}, {cell}, {pins[PIN]},
18
+ {pin_nets} (pin-order nets, space-joined), and {generics[KEY]}.
19
+ Custom lines emit verbatim after substitution; format lines also run
20
+ the language post-pass. An instance with no template at any level is
21
+ skipped silently; a present-but-broken template raises loudly.
22
+ """
23
+
24
+ import re
25
+
26
+ from .connectgraph import ConnectGraphError
27
+
28
+ RAW_IR_VERSION = 1
29
+
30
+ LANGUAGES = ("spice", "verilog", "vhdl")
31
+
32
+ FORMAT_LEVELS = (
33
+ "instance_custom",
34
+ "symbol_custom",
35
+ "instance_format",
36
+ "symbol_format",
37
+ )
38
+
39
+ _VHDL_DIRECTIONS = {"input": "in", "output": "out", "inout": "inout"}
40
+ _BUS_RE = re.compile(r"^(?P<base>.+)\[(?P<bit>\d+)\]$")
41
+ _SPICE_WIDTH = 80
42
+
43
+
44
+ def resolve_format(instance, symbol, language):
45
+ """Resolve one instance template through the 4-level chain.
46
+
47
+ Args:
48
+ instance: Instance mapping with custom/format mappings.
49
+ symbol: Symbol mapping with custom/format mappings.
50
+ language: One of spice, verilog, vhdl.
51
+
52
+ Returns:
53
+ (level, template) with level from FORMAT_LEVELS, or
54
+ (None, None) when no level carries a template.
55
+ """
56
+ candidates = (
57
+ ("instance_custom", (instance.get("custom") or {}).get(language)),
58
+ ("symbol_custom", (symbol.get("custom") or {}).get(language)),
59
+ ("instance_format", (instance.get("format") or {}).get(language)),
60
+ ("symbol_format", (symbol.get("format") or {}).get(language)),
61
+ )
62
+ for level, template in candidates:
63
+ if template:
64
+ return (level, template)
65
+ return (None, None)
66
+
67
+
68
+ def to_raw_ir(structs):
69
+ """Build the language-neutral raw IR from extracted structs.
70
+
71
+ Args:
72
+ structs: NetlistStructs from prepare_netlist_structs.
73
+
74
+ Returns:
75
+ Plain-data mapping (JSON-safe) with cell, nets, ports,
76
+ instances carrying per-language lines, and skipped names.
77
+
78
+ Raises:
79
+ ConnectGraphError: Broken template at a resolved level.
80
+ """
81
+ connectivity = structs.connectivity
82
+ nets = [{"id": net.id, "name": connectivity.net_name(net.id)} for net in connectivity.nets]
83
+ nets.sort(key=lambda n: n["name"])
84
+ instances = []
85
+ skipped = []
86
+ for resolved in structs.instances:
87
+ symbol = structs.symbols[resolved.symbol]
88
+ pin_nets = {
89
+ pin: connectivity.net_name(net_id) for pin, net_id in resolved.pins
90
+ }
91
+ namespace = {
92
+ "name": resolved.name,
93
+ "symbol": resolved.symbol,
94
+ "cell": structs.cell,
95
+ "pins": pin_nets,
96
+ "pin_nets": " ".join(
97
+ pin_nets[pin] for pin, _ in resolved.pins
98
+ ),
99
+ "generics": dict(resolved.generics),
100
+ }
101
+ lines = {}
102
+ resolutions = {}
103
+ view = {"custom": resolved.custom, "format": resolved.format}
104
+ for language in LANGUAGES:
105
+ level, template = resolve_format(view, symbol, language)
106
+ if level is None:
107
+ continue
108
+ try:
109
+ lines[language] = [template.format(**namespace)]
110
+ except (KeyError, IndexError, ValueError) as exc:
111
+ raise ConnectGraphError(
112
+ f"Instance {resolved.name}: broken {language} template "
113
+ f"at {level}: {exc}."
114
+ ) from exc
115
+ resolutions[language] = level
116
+ if not resolutions:
117
+ skipped.append(resolved.name)
118
+ continue
119
+ instances.append(
120
+ {
121
+ "name": resolved.name,
122
+ "symbol": resolved.symbol,
123
+ "pins": [
124
+ {"pin": pin, "net": connectivity.net_name(net_id)}
125
+ for pin, net_id in resolved.pins
126
+ ],
127
+ "generics": dict(resolved.generics),
128
+ "levels": resolutions,
129
+ "lines": lines,
130
+ }
131
+ )
132
+ instances.sort(key=lambda i: i["name"])
133
+ return {
134
+ "tool": "connectgraph",
135
+ "raw_ir_version": RAW_IR_VERSION,
136
+ "cell": structs.cell,
137
+ "nets": nets,
138
+ "ports": [dict(p) for p in structs.ports],
139
+ "instances": instances,
140
+ "skipped": sorted(skipped),
141
+ }
142
+
143
+
144
+ def emit_netlists(raw_ir, languages=None):
145
+ """Emit netlists for the requested languages.
146
+
147
+ Args:
148
+ raw_ir: Raw-IR mapping from to_raw_ir.
149
+ languages: Subset of spice/verilog/vhdl; all when omitted.
150
+
151
+ Returns:
152
+ Mapping of language to netlist text.
153
+
154
+ Raises:
155
+ ConnectGraphError: Unknown language requested.
156
+ """
157
+ wanted = list(LANGUAGES) if languages is None else list(languages)
158
+ unknown = [language for language in wanted if language not in LANGUAGES]
159
+ if unknown:
160
+ raise ConnectGraphError(f"Unknown netlist language(s): {', '.join(unknown)}.")
161
+ printers = {"spice": emit_spice, "verilog": emit_verilog, "vhdl": emit_vhdl}
162
+ return {language: printers[language](raw_ir) for language in wanted}
163
+
164
+
165
+ def emit_spice(raw_ir):
166
+ """Print the SPICE netlist: subckt frame plus raw instance lines.
167
+
168
+ Args:
169
+ raw_ir: Raw-IR mapping from to_raw_ir.
170
+
171
+ Returns:
172
+ Byte-stable SPICE netlist text ending in one newline.
173
+ """
174
+ _check_raw_ir(raw_ir)
175
+ ports = " ".join(p["name"] for p in raw_ir["ports"])
176
+ header = f".subckt {raw_ir['cell']}" + (f" {ports}" if ports else "")
177
+ body = []
178
+ for instance in raw_ir["instances"]:
179
+ for line in instance["lines"].get("spice", []):
180
+ if instance["levels"]["spice"] in ("instance_format", "symbol_format"):
181
+ body.extend(_wrap_continuation(line, _SPICE_WIDTH, "+"))
182
+ else:
183
+ body.append(line)
184
+ return _join_block(
185
+ f"* {raw_ir['cell']} - generated by connectgraph raw IR (do not edit)",
186
+ [header, *body, f".ends {raw_ir['cell']}"],
187
+ )
188
+
189
+
190
+ def emit_verilog(raw_ir):
191
+ """Print the Verilog netlist: module frame plus raw instances.
192
+
193
+ Args:
194
+ raw_ir: Raw-IR mapping from to_raw_ir.
195
+
196
+ Returns:
197
+ Byte-stable Verilog netlist text ending in one newline.
198
+ """
199
+ _check_raw_ir(raw_ir)
200
+ decls = _verilog_port_decls(raw_ir["ports"])
201
+ header = f"module {raw_ir['cell']} ({', '.join(decls)});"
202
+ wires = _bus_declarations(
203
+ [n["name"] for n in raw_ir["nets"]],
204
+ {p["name"] for p in raw_ir["ports"]},
205
+ scalar="wire {name};",
206
+ vector="wire [{hi}:{lo}] {base};",
207
+ )
208
+ body = []
209
+ for instance in raw_ir["instances"]:
210
+ body.extend(instance["lines"].get("verilog", []))
211
+ return _join_block(
212
+ f"// {raw_ir['cell']} - generated by connectgraph raw IR (do not edit)",
213
+ [header, *wires, *body, "endmodule"],
214
+ )
215
+
216
+
217
+ def emit_vhdl(raw_ir):
218
+ """Print the VHDL netlist: entity plus raw port-map instances.
219
+
220
+ Args:
221
+ raw_ir: Raw-IR mapping from to_raw_ir.
222
+
223
+ Returns:
224
+ Byte-stable VHDL netlist text ending in one newline.
225
+ """
226
+ _check_raw_ir(raw_ir)
227
+ ports = "; ".join(
228
+ f"{p['name']} : {_VHDL_DIRECTIONS[p['direction']]} std_logic"
229
+ for p in raw_ir["ports"]
230
+ )
231
+ signals = _bus_declarations(
232
+ [n["name"] for n in raw_ir["nets"]],
233
+ {p["name"] for p in raw_ir["ports"]},
234
+ scalar="signal {name} : std_logic;",
235
+ vector="signal {base} : std_logic_vector({hi} downto {lo});",
236
+ )
237
+ body = []
238
+ for instance in raw_ir["instances"]:
239
+ body.extend(instance["lines"].get("vhdl", []))
240
+ return _join_block(
241
+ f"-- {raw_ir['cell']} - generated by connectgraph raw IR (do not edit)",
242
+ [
243
+ "library ieee;",
244
+ "use ieee.std_logic_1164.all;",
245
+ f"entity {raw_ir['cell']} is",
246
+ f"port ({ports});" if ports else "port ();",
247
+ f"end entity {raw_ir['cell']};",
248
+ f"architecture raw of {raw_ir['cell']} is",
249
+ *signals,
250
+ "begin",
251
+ *body,
252
+ f"end architecture raw;",
253
+ ],
254
+ )
255
+
256
+
257
+ def _check_raw_ir(raw_ir):
258
+ if (
259
+ not isinstance(raw_ir, dict)
260
+ or raw_ir.get("raw_ir_version") != RAW_IR_VERSION
261
+ or not isinstance(raw_ir.get("instances"), list)
262
+ ):
263
+ raise ConnectGraphError(
264
+ "Emitters accept raw-IR mappings from to_raw_ir only."
265
+ )
266
+
267
+
268
+ def _join_block(comment, lines):
269
+ return comment + "\n" + "\n".join(lines) + "\n"
270
+
271
+
272
+ def _wrap_continuation(line, width, marker):
273
+ """Wrap one line with continuation markers (the line joinery)."""
274
+ if len(line) <= width:
275
+ return [line]
276
+ words = line.split(" ")
277
+ chunks = [words[0]]
278
+ for word in words[1:]:
279
+ if len(chunks[-1]) + 1 + len(word) > width and chunks[-1]:
280
+ chunks.append(marker + word)
281
+ else:
282
+ chunks[-1] = chunks[-1] + " " + word if chunks[-1] else word
283
+ fixed = []
284
+ for chunk in chunks[1:]:
285
+ fixed.append(chunk if chunk.startswith(marker) else marker + chunk)
286
+ return [chunks[0], *fixed]
287
+
288
+
289
+ def _bus_groups(names, ports):
290
+ """Expand bus-bit names into one declaration group each.
291
+
292
+ Scalars yield ("scalar", name); bits base[i] fold into one
293
+ ("vector", base, lo, hi). Groups fully covered by ports are
294
+ dropped — the port header already declares them.
295
+ """
296
+ bits = {}
297
+ scalars = set()
298
+ for name in names:
299
+ match = _BUS_RE.match(name)
300
+ if match:
301
+ bits.setdefault(match.group("base"), []).append(
302
+ (int(match.group("bit")), name)
303
+ )
304
+ else:
305
+ scalars.add(name)
306
+ groups = []
307
+ for base in sorted(set(bits) | scalars):
308
+ if base in scalars and base not in bits:
309
+ if base not in ports:
310
+ groups.append(("scalar", base))
311
+ continue
312
+ member_names = [name for _, name in bits.get(base, [])]
313
+ if member_names and all(name in ports for name in member_names):
314
+ continue
315
+ lo = min(bit for bit, _ in bits[base])
316
+ hi = max(bit for bit, _ in bits[base])
317
+ groups.append(("vector", base, lo, hi))
318
+ return groups
319
+
320
+
321
+ def _bus_declarations(names, ports, scalar, vector):
322
+ """Declare nets once: scalars flat, bus bits as one vector."""
323
+ declarations = []
324
+ for group in _bus_groups(names, ports):
325
+ if group[0] == "scalar":
326
+ declarations.append(scalar.format(name=group[1]))
327
+ else:
328
+ declarations.append(
329
+ vector.format(base=group[1], hi=group[3], lo=group[2])
330
+ )
331
+ return declarations
332
+
333
+
334
+ def _verilog_port_decls(ports):
335
+ kinds = {"input": "input", "output": "output", "inout": "inout"}
336
+ return [f"{kinds[p['direction']]} {p['name']}" for p in ports]
@@ -0,0 +1,144 @@
1
+ """Target+canvas gating over a flow plan (WIRE-DECLARED-OPS MC-25).
2
+
3
+ `plan_checks_report` plans a flow against backends only; this
4
+ module scopes that plan to a v1 target and canvas without
5
+ changing readiness. Every stage names its deck tools (joined
6
+ through the stages DECKS table), the canvas tool settings for
7
+ those tools, and the project-top metric budgets it must
8
+ satisfy — cost (settings) and evidence travel with the plan.
9
+ Constraints naming a `target` stay plan-level (`scoped_targets`)
10
+ for the run to scope; only project-top budgets gate stages.
11
+ """
12
+
13
+ from .stages import DECKS
14
+
15
+
16
+ def _target_or_raise(target):
17
+ """Clean one v1 target to its kind plus project scope.
18
+
19
+ Args:
20
+ target: Target mapping with a non-empty kind.
21
+
22
+ Returns:
23
+ Cleaned target with kind plus project_id/cell when given.
24
+
25
+ Raises:
26
+ ValueError: Non-mapping target or a missing kind.
27
+ """
28
+ if not isinstance(target, dict) or not isinstance(
29
+ target.get("kind"), str
30
+ ) or not target["kind"]:
31
+ raise ValueError(f"Plan target needs a kind: {target!r}.")
32
+ clean = {"kind": target["kind"]}
33
+ for key in ("project_id", "cell"):
34
+ if target.get(key) is not None:
35
+ clean[key] = target[key]
36
+ return clean
37
+
38
+
39
+ def _canvas_or_raise(canvas):
40
+ """Clean one canvas to metric budgets plus tool settings.
41
+
42
+ Args:
43
+ canvas: Canvas mapping with optional `targets`
44
+ (MetricConstraint list) and `tools` (tool settings);
45
+ None means an empty canvas.
46
+
47
+ Returns:
48
+ (budgets, scoped, tools): project-top budget mappings,
49
+ target-scoped (metric, target) pairs, and the tool
50
+ settings mapping.
51
+
52
+ Raises:
53
+ ValueError: Non-mapping canvas, a non-list targets, or
54
+ a non-mapping tools.
55
+ """
56
+ if canvas is None:
57
+ return [], [], {}
58
+ if not isinstance(canvas, dict):
59
+ raise ValueError(f"Plan canvas must be a mapping: {canvas!r}.")
60
+ targets = canvas.get("targets", [])
61
+ tools = canvas.get("tools", {})
62
+ if not isinstance(targets, list) or any(
63
+ not isinstance(item, dict) or not item.get("metric")
64
+ for item in targets
65
+ ):
66
+ raise ValueError(f"Plan canvas targets malformed: {targets!r}.")
67
+ if not isinstance(tools, dict):
68
+ raise ValueError(f"Plan canvas tools malformed: {tools!r}.")
69
+ budgets = [
70
+ {"metric": item["metric"], "operator": item.get("operator"),
71
+ "limit": item.get("limit")}
72
+ for item in targets
73
+ if item.get("target") is None
74
+ ]
75
+ scoped = [
76
+ {"metric": item["metric"], "target": item["target"]}
77
+ for item in targets
78
+ if item.get("target") is not None
79
+ ]
80
+ return budgets, scoped, dict(tools)
81
+
82
+
83
+ def _stage_tools(stage):
84
+ """Sorted deck-tool names serving one stage's entrypoints.
85
+
86
+ Args:
87
+ stage: Planned stage with entrypoints.
88
+
89
+ Returns:
90
+ Sorted tool names from the DECKS table (scripted and
91
+ uncovered entrypoints contribute none).
92
+ """
93
+ tools = set()
94
+ for entrypoint in stage.get("entrypoints", []):
95
+ for cite in DECKS.get(entrypoint, []):
96
+ tools.add(cite["tool"])
97
+ return sorted(tools)
98
+
99
+
100
+ def gate_plan_for_target(plan, target, canvas=None):
101
+ """Scope one flow plan to a target and canvas.
102
+
103
+ Readiness is untouched — the gate only joins cost and
104
+ evidence: per-stage deck tools, the canvas tool settings for
105
+ those tools, and the project-top metric budgets every stage
106
+ must satisfy.
107
+
108
+ Args:
109
+ plan: Flow plan mapping with flow plus stages (as from
110
+ plan_checks_report).
111
+ target: v1 target the plan serves (kind required).
112
+ canvas: Canvas mapping with targets/tools; None means an
113
+ empty canvas.
114
+
115
+ Returns:
116
+ Plan mapping plus target, canvas_metrics, canvas_tools,
117
+ scoped_targets, and per-stage target_kind, tools,
118
+ canvas_tools, and budgets.
119
+
120
+ Raises:
121
+ ValueError: Bad target or canvas shape.
122
+ """
123
+ clean_target = _target_or_raise(target)
124
+ budgets, scoped, tools = _canvas_or_raise(canvas)
125
+ stages = []
126
+ for stage in plan.get("stages", []):
127
+ stage_tools = _stage_tools(stage)
128
+ stages.append({
129
+ **stage,
130
+ "target_kind": clean_target["kind"],
131
+ "tools": stage_tools,
132
+ "canvas_tools": {tool: tools[tool] for tool in stage_tools
133
+ if tool in tools},
134
+ "budgets": [dict(budget) for budget in budgets],
135
+ })
136
+ return {
137
+ **plan,
138
+ "target": clean_target,
139
+ "canvas_metrics": [b["metric"] for b in budgets]
140
+ + [s["metric"] for s in scoped],
141
+ "canvas_tools": sorted(tools),
142
+ "scoped_targets": scoped,
143
+ "stages": stages,
144
+ }
@@ -0,0 +1,76 @@
1
+ """PHOTONIC-WIRE lane: register the proven photonic solvers on an Engine.
2
+
3
+ Circulax (time-domain transient circuit sim) serves run_optical
4
+ behind deck.tool `circulax` as the second tool — sax stays first,
5
+ so the default never moves. Meow (EME mode/field solving with
6
+ two-resolution convergence evidence) serves run_em behind
7
+ deck.tool `meow` as the only tool. FDTD/FEM stay out: proposed,
8
+ not proven, so no other deck tool is registered here.
9
+
10
+ This helper is the executable form of the composition.py wiring
11
+ patch (see docs/work/PHOTONIC-WIRE/report.md): it performs the
12
+ same solver registrations the patch bakes into Engine.__init__,
13
+ plus the same SOLVER_DEPS detection rows the patch adds to
14
+ interfaces/setup.py, so dispatch and capabilities reporting are
15
+ provable on an unpatched tree. Post-patch it is an idempotent
16
+ re-registration of identical keys.
17
+
18
+ Generalization law: the adapters consume project specs (circuit
19
+ YAML in, transient out; device YAML in, S-params plus
20
+ convergence out). The RC/EME smokes qualify the installs; this
21
+ lane generalizes them and never hardcodes their values.
22
+ """
23
+
24
+ from ..interfaces.setup import SOLVER_DEPS
25
+
26
+ PHOTONIC_TOOLS = {"run_optical": "circulax", "run_em": "meow"}
27
+
28
+ # Detection rows mirroring the setup.py patch: each cites the
29
+ # solver's own import probe, the way the sax row cites sax. Deeper
30
+ # pip-transitive deps stay out — a solver present but broken fails
31
+ # loud at run time, never as a misreported capability.
32
+ PHOTONIC_DEPS = {
33
+ ("run_optical", "circulax"): {"python": ["circulax"]},
34
+ ("run_em", "meow"): {"python": ["meow"]},
35
+ }
36
+
37
+
38
+ def wire_photonics(engine, *, runner=None):
39
+ """Register the circulax and meow solvers on an Engine.
40
+
41
+ Circulax joins run_optical after sax (the default is the
42
+ first registered, so sax keeps it); meow opens run_em.
43
+ Detection rows land in SOLVER_DEPS only where the static
44
+ table has none, so an integrator-patched tree keeps its own
45
+ rows and capabilities reporting never raises unknown_solver
46
+ for a wired lane.
47
+
48
+ Args:
49
+ engine: Engine to wire (composition root with
50
+ register_solver).
51
+ runner: Optional isolated runner handed to both
52
+ adapters; direct subprocess when None.
53
+
54
+ Returns:
55
+ Mapping of operation to deck-tool key registered
56
+ ({"run_optical": "circulax", "run_em": "meow"}).
57
+
58
+ Raises:
59
+ ModuleNotFoundError: If the same-ticket adapter modules
60
+ (adapters.circulax.adapter, adapters.meow.adapter)
61
+ are not importable — the lane is missing, never
62
+ faked.
63
+
64
+ Caution: Mutates the shared SOLVER_DEPS map (additive rows
65
+ only, never overwrites) and the engine's solver registry
66
+ — call once per Engine; repeats re-register identically.
67
+ """
68
+ from ..adapters.circulax.adapter import CirculaxAdapter
69
+ from ..adapters.meow.adapter import MeowAdapter
70
+
71
+ for key, spec in PHOTONIC_DEPS.items():
72
+ SOLVER_DEPS.setdefault(key, dict(spec))
73
+ engine.register_solver("run_optical", CirculaxAdapter(runner=runner),
74
+ tool="circulax")
75
+ engine.register_solver("run_em", MeowAdapter(runner=runner), tool="meow")
76
+ return dict(PHOTONIC_TOOLS)