PySHDL 0.3.0__tar.gz → 0.3.1__tar.gz
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.
- {pyshdl-0.3.0 → pyshdl-0.3.1}/PKG-INFO +1 -1
- {pyshdl-0.3.0 → pyshdl-0.3.1}/pyproject.toml +1 -1
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/bus_compiler/analyzer.py +24 -5
- pyshdl-0.3.1/src/SHDL/bus_compiler/compiler.py +137 -0
- pyshdl-0.3.1/src/SHDL/bus_compiler/debug_codegen.py +313 -0
- pyshdl-0.3.1/src/SHDL/bus_compiler/debug_info_gen.py +96 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/debugger/circuit.py +17 -18
- pyshdl-0.3.1/tests/test_bus_compiler.py +683 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/uv.lock +1 -1
- pyshdl-0.3.0/src/SHDL/bus_compiler/compiler.py +0 -63
- {pyshdl-0.3.0 → pyshdl-0.3.1}/.github/workflows/deploy-docs.yml +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/.github/workflows/publish.yml +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/.github/workflows/test.yml +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/.gitignore +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/LICENSE +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/README.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/README.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/architecture/_category_.json +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/architecture/base-shdl.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/architecture/compiler-internals.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/architecture/flattening-pipeline.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/architecture/overview.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/architecture/pyshdl-internals.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/_category_.json +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/breakpoints.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/commands.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/common-problems.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/debug-build.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/getting-started.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/hierarchy.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/inspection.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/overview.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/python-api.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/scripting.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/debugger/waveforms.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/examples/8-bit-adder.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/examples/_category_.json +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/examples/comparator.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/examples/decoder.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/examples/full-adder.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/examples/half-adder.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/examples/multiplexer.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/examples/register.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/getting-started/_category_.json +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/getting-started/first-circuit.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/getting-started/installation.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/getting-started/using-pyshdl.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/intro.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/_category_.json +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/components.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/connections.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/constants.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/errors.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/generators.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/imports.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/lexical-elements.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/overview.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/signals.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docs/language-reference/standard-gates.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/docusaurus.config.ts +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/package.json +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/sidebars.ts +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/src/components/HomepageFeatures/index.tsx +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/src/components/HomepageFeatures/styles.module.css +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/src/css/custom.css +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/src/pages/index.module.css +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/src/pages/index.tsx +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/src/pages/markdown-page.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/.nojekyll +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/img/docusaurus-social-card.jpg +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/img/docusaurus.png +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/img/favicon.ico +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/img/halfAdder.png +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/img/logo.svg +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/img/undraw_docusaurus_mountain.svg +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/img/undraw_docusaurus_react.svg +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/static/img/undraw_docusaurus_tree.svg +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/docs/tsconfig.json +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/add100.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/addSub16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/adder16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/alu.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/and16inputs.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/bitwise_and16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/bitwise_not16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/bitwise_or16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/bitwise_xor16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/clock.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/demux.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/flagsZN.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/fullAdder.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/mux2.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/mux2_16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/mux8.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/mux8_16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/negate16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/or16inputs.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/reg16.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/SHDL_components/shift1.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/examples/interacting.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDB/__init__.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/__init__.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/bus_compiler/__init__.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/bus_compiler/codegen.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/bus_compiler/graph.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/__init__.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/analyzer.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/ast.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/cli.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/codegen.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/compiler.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/debug_codegen.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/debug_info_gen.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/lexer.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/compiler/parser.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/debugger/__init__.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/debugger/cli.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/debugger/controller.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/debugger/debuginfo.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/debugger/sourcemap.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/debugger/symbols.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/driver/__init__.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/driver/circuit.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/driver/exceptions.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/errors.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/flattener/__init__.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/flattener/ast.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/flattener/flattener.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/flattener/lexer.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/flattener/parser.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/flattener/tokens.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/py.typed +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/semantic/__init__.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/semantic/analyzer.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/semantic/connection.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/semantic/resolver.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/semantic/type_check.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/semantic/warnings.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/src/SHDL/source_map.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/README.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/TEST_REPORT.md +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_adder4.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_adder8.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_bitwise.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_comparator.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_constants.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_decoder.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_gates.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_generators.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_half_full_adder.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/circuits/test_mux.shdl +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/conftest.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/test_debugger.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/test_errors.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/test_shdl.py +0 -0
- {pyshdl-0.3.0 → pyshdl-0.3.1}/tests/test_shdl_comprehensive.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PySHDL
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: SHDL (Simple Hardware Description Language) is a minimal HDL designed for creating digital circuits and easily simulating them. It compiles directly to C for maximum performance and portability. PySHDL is the Python interface for SHDL.
|
|
5
5
|
Project-URL: Homepage, https://github.com/rafa-rrayes/SHDL
|
|
6
6
|
Project-URL: Repository, https://github.com/rafa-rrayes/SHDL
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "PySHDL"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "SHDL (Simple Hardware Description Language) is a minimal HDL designed for creating digital circuits and easily simulating them. It compiles directly to C for maximum performance and portability. PySHDL is the Python interface for SHDL."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -114,12 +114,18 @@ class BusAnalyzer:
|
|
|
114
114
|
grouped_gates.add(gate.name)
|
|
115
115
|
gate_group_map[gate.name] = name
|
|
116
116
|
|
|
117
|
+
# Build position map: gate_name -> (group_name, position_in_group)
|
|
118
|
+
gate_position_map: dict[str, tuple[str, int]] = {}
|
|
119
|
+
for group in bus_groups:
|
|
120
|
+
for pos, gate in enumerate(group.gates):
|
|
121
|
+
gate_position_map[gate.name] = (group.name, pos)
|
|
122
|
+
|
|
117
123
|
# Singletons
|
|
118
124
|
singletons = [g for g in all_gates if g.name not in grouped_gates]
|
|
119
125
|
|
|
120
126
|
# Step 4: Classify sources for each bus group
|
|
121
127
|
for group in bus_groups:
|
|
122
|
-
self._classify_sources(group, gate_group_map)
|
|
128
|
+
self._classify_sources(group, gate_group_map, gate_position_map)
|
|
123
129
|
|
|
124
130
|
# Step 5: SCC detection
|
|
125
131
|
scc_map = self._detect_feedback(bus_groups)
|
|
@@ -215,7 +221,8 @@ class BusAnalyzer:
|
|
|
215
221
|
|
|
216
222
|
return positions
|
|
217
223
|
|
|
218
|
-
def _classify_sources(self, group: BusGroup, gate_group_map: dict[str, str]
|
|
224
|
+
def _classify_sources(self, group: BusGroup, gate_group_map: dict[str, str],
|
|
225
|
+
gate_position_map: dict[str, tuple[str, int]]):
|
|
219
226
|
"""Classify each input port source for a bus group."""
|
|
220
227
|
input_port_names = ["A", "B"] if group.primitive != "NOT" else ["A"]
|
|
221
228
|
|
|
@@ -228,12 +235,14 @@ class BusAnalyzer:
|
|
|
228
235
|
if not wires or wires[0] is None:
|
|
229
236
|
continue
|
|
230
237
|
|
|
231
|
-
source = self._classify_wire_list(wires, group.bit_indices, gate_group_map
|
|
238
|
+
source = self._classify_wire_list(wires, group.bit_indices, gate_group_map,
|
|
239
|
+
gate_position_map)
|
|
232
240
|
group.input_sources[port_name] = source
|
|
233
241
|
|
|
234
242
|
def _classify_wire_list(
|
|
235
243
|
self, wires: list[WireRef], bit_indices: list[int],
|
|
236
|
-
gate_group_map: dict[str, str]
|
|
244
|
+
gate_group_map: dict[str, str],
|
|
245
|
+
gate_position_map: dict[str, tuple[str, int]]
|
|
237
246
|
) -> BusSource:
|
|
238
247
|
"""Classify a list of wires (one per gate in the group)."""
|
|
239
248
|
if not wires or wires[0] is None:
|
|
@@ -275,7 +284,17 @@ class BusAnalyzer:
|
|
|
275
284
|
|
|
276
285
|
if len(src_groups) == 1 and None not in src_groups:
|
|
277
286
|
group_name = src_groups.pop()
|
|
278
|
-
|
|
287
|
+
# Verify sequential alignment: source positions must be 0,1,2,...
|
|
288
|
+
positions = []
|
|
289
|
+
misaligned = False
|
|
290
|
+
for w in wires:
|
|
291
|
+
info = gate_position_map.get(w.name)
|
|
292
|
+
if info is None or info[0] != group_name:
|
|
293
|
+
misaligned = True
|
|
294
|
+
break
|
|
295
|
+
positions.append(info[1])
|
|
296
|
+
if not misaligned and positions == list(range(len(positions))):
|
|
297
|
+
return BusSource(kind="bus_group", ref=group_name)
|
|
279
298
|
|
|
280
299
|
# Fallback: mixed
|
|
281
300
|
return BusSource(kind="mixed", per_bit=list(wires))
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bus Compiler Orchestration.
|
|
3
|
+
|
|
4
|
+
Pipeline: Flattened Component -> ConnectionGraph -> BusAnalyzer -> BusCodeGenerator -> clang
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import subprocess
|
|
9
|
+
import tempfile
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from ..compiler.compiler import CompileResult
|
|
14
|
+
from .graph import ConnectionGraph
|
|
15
|
+
from .analyzer import BusAnalyzer
|
|
16
|
+
from .codegen import BusCodeGenerator
|
|
17
|
+
from .debug_codegen import BusDebugCodeGenerator
|
|
18
|
+
from .debug_info_gen import BusDebugInfoBuilder
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BusCompiler:
|
|
22
|
+
"""Compiles a flattened Component to C using bus-width operations."""
|
|
23
|
+
|
|
24
|
+
def compile(self, component) -> str:
|
|
25
|
+
"""Generate C code from a flattened Component (expanded AST)."""
|
|
26
|
+
graph = ConnectionGraph.from_component(component)
|
|
27
|
+
analysis = BusAnalyzer(graph).analyze()
|
|
28
|
+
return BusCodeGenerator(analysis).generate()
|
|
29
|
+
|
|
30
|
+
def compile_debug(self, component) -> str:
|
|
31
|
+
"""Generate C code with debug API from a flattened Component."""
|
|
32
|
+
graph = ConnectionGraph.from_component(component)
|
|
33
|
+
analysis = BusAnalyzer(graph).analyze()
|
|
34
|
+
return BusDebugCodeGenerator(analysis).generate()
|
|
35
|
+
|
|
36
|
+
def _analyze(self, component):
|
|
37
|
+
"""Run the analysis pipeline, returning the AnalysisResult."""
|
|
38
|
+
graph = ConnectionGraph.from_component(component)
|
|
39
|
+
return BusAnalyzer(graph).analyze()
|
|
40
|
+
|
|
41
|
+
def compile_to_library(
|
|
42
|
+
self,
|
|
43
|
+
component,
|
|
44
|
+
output_path: str,
|
|
45
|
+
cc: str = "clang",
|
|
46
|
+
cflags: Optional[list[str]] = None,
|
|
47
|
+
) -> CompileResult:
|
|
48
|
+
"""Compile a flattened Component to a shared library."""
|
|
49
|
+
c_code = self.compile(component)
|
|
50
|
+
|
|
51
|
+
with tempfile.NamedTemporaryFile(
|
|
52
|
+
mode='w', suffix='.c', delete=False
|
|
53
|
+
) as f:
|
|
54
|
+
f.write(c_code)
|
|
55
|
+
c_path = f.name
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
default_flags = ["-O3", "-shared", "-fPIC"]
|
|
59
|
+
all_flags = default_flags + (cflags or [])
|
|
60
|
+
cmd = [cc] + all_flags + ["-o", output_path, c_path]
|
|
61
|
+
|
|
62
|
+
proc = subprocess.run(cmd, capture_output=True, text=True)
|
|
63
|
+
|
|
64
|
+
if proc.returncode != 0:
|
|
65
|
+
return CompileResult(
|
|
66
|
+
success=False,
|
|
67
|
+
c_code=c_code,
|
|
68
|
+
errors=[f"C compilation failed: {proc.stderr}"],
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return CompileResult(
|
|
72
|
+
success=True,
|
|
73
|
+
c_code=c_code,
|
|
74
|
+
library_path=output_path,
|
|
75
|
+
)
|
|
76
|
+
finally:
|
|
77
|
+
os.unlink(c_path)
|
|
78
|
+
|
|
79
|
+
def compile_to_library_debug(
|
|
80
|
+
self,
|
|
81
|
+
component,
|
|
82
|
+
output_path: str,
|
|
83
|
+
component_name: str = "",
|
|
84
|
+
source_path: str = "",
|
|
85
|
+
cc: str = "clang",
|
|
86
|
+
cflags: Optional[list[str]] = None,
|
|
87
|
+
generate_shdb: bool = True,
|
|
88
|
+
) -> CompileResult:
|
|
89
|
+
"""Compile a flattened Component to a shared library with debug support.
|
|
90
|
+
|
|
91
|
+
Produces:
|
|
92
|
+
- A shared library with peek_gate, peek_gate_previous, get_cycle
|
|
93
|
+
- A .shdb file with gate names, ports, hierarchy info
|
|
94
|
+
"""
|
|
95
|
+
analysis = self._analyze(component)
|
|
96
|
+
c_code = BusDebugCodeGenerator(analysis).generate()
|
|
97
|
+
|
|
98
|
+
with tempfile.NamedTemporaryFile(
|
|
99
|
+
mode='w', suffix='.c', delete=False
|
|
100
|
+
) as f:
|
|
101
|
+
f.write(c_code)
|
|
102
|
+
c_path = f.name
|
|
103
|
+
|
|
104
|
+
debug_info_path = None
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
# Generate .shdb if requested
|
|
108
|
+
if generate_shdb:
|
|
109
|
+
lib_path = Path(output_path)
|
|
110
|
+
shdb_path = lib_path.with_suffix('.shdb')
|
|
111
|
+
builder = BusDebugInfoBuilder(analysis, source_file=source_path)
|
|
112
|
+
builder.set_component_name(component_name)
|
|
113
|
+
builder.save(str(shdb_path))
|
|
114
|
+
debug_info_path = str(shdb_path)
|
|
115
|
+
|
|
116
|
+
# Compile with -g for C debug symbols, -O1 for debug builds
|
|
117
|
+
default_flags = ["-g", "-O1", "-shared", "-fPIC"]
|
|
118
|
+
all_flags = default_flags + (cflags or [])
|
|
119
|
+
cmd = [cc] + all_flags + ["-o", output_path, c_path]
|
|
120
|
+
|
|
121
|
+
proc = subprocess.run(cmd, capture_output=True, text=True)
|
|
122
|
+
|
|
123
|
+
if proc.returncode != 0:
|
|
124
|
+
return CompileResult(
|
|
125
|
+
success=False,
|
|
126
|
+
c_code=c_code,
|
|
127
|
+
errors=[f"C compilation failed: {proc.stderr}"],
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return CompileResult(
|
|
131
|
+
success=True,
|
|
132
|
+
c_code=c_code,
|
|
133
|
+
library_path=output_path,
|
|
134
|
+
debug_info_path=debug_info_path,
|
|
135
|
+
)
|
|
136
|
+
finally:
|
|
137
|
+
os.unlink(c_path)
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Debug-Aware Bus Code Generator.
|
|
3
|
+
|
|
4
|
+
Extends BusCodeGenerator with debug features:
|
|
5
|
+
- Gate name table mapping gate names to group/singleton + bit position
|
|
6
|
+
- peek_gate() / peek_gate_previous() for inspecting internal gates
|
|
7
|
+
- Cycle counter and get_cycle()
|
|
8
|
+
- Previous state tracking for breakpoint change detection
|
|
9
|
+
|
|
10
|
+
Key difference from parent: all bus group and singleton gate results are stored
|
|
11
|
+
in static file-scope variables (not locals in tick), so peek_gate can read them.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from ..compiler.ast import PrimitiveType
|
|
15
|
+
from .codegen import BusCodeGenerator, _select_c_type, _width_mask
|
|
16
|
+
from .analyzer import AnalysisResult, BusGroup
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BusDebugCodeGenerator(BusCodeGenerator):
|
|
20
|
+
"""Bus code generator with debug API additions.
|
|
21
|
+
|
|
22
|
+
Overrides _emit_group_eval and _emit_unit to assign to static globals
|
|
23
|
+
instead of declaring locals, so gate values persist after tick() returns.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, analysis: AnalysisResult):
|
|
27
|
+
super().__init__(analysis)
|
|
28
|
+
self._group_list: list[BusGroup] = []
|
|
29
|
+
self._group_idx: dict[str, int] = {}
|
|
30
|
+
|
|
31
|
+
def generate(self) -> str:
|
|
32
|
+
self._emit_header()
|
|
33
|
+
self._emit_state_struct()
|
|
34
|
+
self._emit_gate_globals()
|
|
35
|
+
self._emit_dut_context_debug()
|
|
36
|
+
self._emit_gate_table()
|
|
37
|
+
# Parent's tick — our overrides make it assign to statics
|
|
38
|
+
self._emit_tick_function()
|
|
39
|
+
self._emit_api_functions_debug()
|
|
40
|
+
self._emit_debug_api()
|
|
41
|
+
return self.output.getvalue()
|
|
42
|
+
|
|
43
|
+
# ── Gate globals (static file-scope vars) ──
|
|
44
|
+
|
|
45
|
+
def _emit_gate_globals(self):
|
|
46
|
+
"""Emit static variables for all groups and singletons."""
|
|
47
|
+
self._group_list = list(self.analysis.bus_groups)
|
|
48
|
+
self._group_idx = {g.name: i for i, g in enumerate(self._group_list)}
|
|
49
|
+
|
|
50
|
+
self._w("/* Bus group values (persist between tick calls for debug) */")
|
|
51
|
+
for group in self._group_list:
|
|
52
|
+
c_type = _select_c_type(group.width)
|
|
53
|
+
self._w(f"static {c_type} {group.name};")
|
|
54
|
+
self._w(f"static {c_type} dbg_prev_{group.name};")
|
|
55
|
+
|
|
56
|
+
if self.analysis.singleton_gates:
|
|
57
|
+
self._w()
|
|
58
|
+
self._w("/* Singleton gate values */")
|
|
59
|
+
for gate in self.analysis.singleton_gates:
|
|
60
|
+
self._w(f"static uint8_t s_{gate.name};")
|
|
61
|
+
self._w(f"static uint8_t dbg_prev_s_{gate.name};")
|
|
62
|
+
|
|
63
|
+
self._w()
|
|
64
|
+
|
|
65
|
+
# ── DUT context (debug version with previous + cycle) ──
|
|
66
|
+
|
|
67
|
+
def _emit_dut_context_debug(self):
|
|
68
|
+
self._w("typedef struct {")
|
|
69
|
+
self._indent()
|
|
70
|
+
self._w("State current;")
|
|
71
|
+
self._w("State previous;")
|
|
72
|
+
for name, width in self.analysis.input_ports.items():
|
|
73
|
+
self._w(f"{_select_c_type(width)} input_{name};")
|
|
74
|
+
for name, width in self.analysis.output_ports.items():
|
|
75
|
+
self._w(f"{_select_c_type(width)} output_{name};")
|
|
76
|
+
self._w("int outputs_valid;")
|
|
77
|
+
self._w("uint64_t cycle_count;")
|
|
78
|
+
self._dedent()
|
|
79
|
+
self._w("} DutContext;")
|
|
80
|
+
self._w()
|
|
81
|
+
self._w("static DutContext dut = {0};")
|
|
82
|
+
self._w()
|
|
83
|
+
|
|
84
|
+
# ── Gate table ──
|
|
85
|
+
|
|
86
|
+
def _emit_gate_table(self):
|
|
87
|
+
"""Emit gate lookup table: name -> (is_group, index, bit_pos)."""
|
|
88
|
+
self._w("typedef struct {")
|
|
89
|
+
self._indent()
|
|
90
|
+
self._w("const char *name;")
|
|
91
|
+
self._w("uint8_t is_group;")
|
|
92
|
+
self._w("uint16_t idx;")
|
|
93
|
+
self._w("uint8_t bit_pos;")
|
|
94
|
+
self._dedent()
|
|
95
|
+
self._w("} GateEntry;")
|
|
96
|
+
self._w()
|
|
97
|
+
|
|
98
|
+
entries: list[tuple[str, int, int, int]] = []
|
|
99
|
+
|
|
100
|
+
for group in self._group_list:
|
|
101
|
+
gidx = self._group_idx[group.name]
|
|
102
|
+
for pos, gate in enumerate(group.gates):
|
|
103
|
+
entries.append((gate.name, 1, gidx, pos))
|
|
104
|
+
|
|
105
|
+
for i, gate in enumerate(self.analysis.singleton_gates):
|
|
106
|
+
entries.append((gate.name, 0, i, 0))
|
|
107
|
+
|
|
108
|
+
self._w("static const GateEntry GATE_TABLE[] = {")
|
|
109
|
+
self._indent()
|
|
110
|
+
for name, is_grp, idx, bit in entries:
|
|
111
|
+
self._w(f'{{"{name}", {is_grp}, {idx}, {bit}}},')
|
|
112
|
+
if not entries:
|
|
113
|
+
self._w('{NULL, 0, 0, 0},')
|
|
114
|
+
self._dedent()
|
|
115
|
+
self._w("};")
|
|
116
|
+
self._w(f"static const size_t NUM_GATES = {len(entries)};")
|
|
117
|
+
self._w()
|
|
118
|
+
|
|
119
|
+
# ── Override group/unit emit to assign to statics ──
|
|
120
|
+
|
|
121
|
+
def _emit_group_eval(self, group: BusGroup, is_feedback: bool):
|
|
122
|
+
"""Assign to static global instead of declaring a local."""
|
|
123
|
+
var = group.name
|
|
124
|
+
self._group_vars[group.name] = var
|
|
125
|
+
|
|
126
|
+
ptype = PrimitiveType.from_string(group.primitive)
|
|
127
|
+
mask = _width_mask(group.width)
|
|
128
|
+
|
|
129
|
+
a_expr = self._source_to_expr(group.input_sources.get("A"), group, is_feedback)
|
|
130
|
+
b_expr = self._source_to_expr(group.input_sources.get("B"), group, is_feedback)
|
|
131
|
+
|
|
132
|
+
if ptype == PrimitiveType.NOT:
|
|
133
|
+
self._w(f"{var} = (~({a_expr})) & {mask};")
|
|
134
|
+
elif ptype == PrimitiveType.AND:
|
|
135
|
+
self._w(f"{var} = ({a_expr}) & ({b_expr});")
|
|
136
|
+
elif ptype == PrimitiveType.OR:
|
|
137
|
+
self._w(f"{var} = ({a_expr}) | ({b_expr});")
|
|
138
|
+
elif ptype == PrimitiveType.XOR:
|
|
139
|
+
self._w(f"{var} = ({a_expr}) ^ ({b_expr});")
|
|
140
|
+
|
|
141
|
+
def _emit_unit(self, unit):
|
|
142
|
+
"""Assign to static global instead of declaring a local."""
|
|
143
|
+
if isinstance(unit, BusGroup):
|
|
144
|
+
self._emit_group_eval(unit, is_feedback=False)
|
|
145
|
+
else:
|
|
146
|
+
var = f"s_{unit.name}"
|
|
147
|
+
self._singleton_vars[unit.name] = var
|
|
148
|
+
expr = self._build_singleton_expr(unit)
|
|
149
|
+
self._w(f"{var} = {expr};")
|
|
150
|
+
|
|
151
|
+
# ── API functions (debug versions) ──
|
|
152
|
+
|
|
153
|
+
def _emit_api_functions_debug(self):
|
|
154
|
+
self._emit_reset_debug()
|
|
155
|
+
self._emit_poke() # parent's
|
|
156
|
+
self._emit_peek() # parent's
|
|
157
|
+
self._emit_step_debug()
|
|
158
|
+
|
|
159
|
+
def _emit_reset_debug(self):
|
|
160
|
+
self._w("void reset(void) {")
|
|
161
|
+
self._indent()
|
|
162
|
+
self._w("memset(&dut, 0, sizeof(dut));")
|
|
163
|
+
# Zero all static gate variables
|
|
164
|
+
for group in self._group_list:
|
|
165
|
+
self._w(f"{group.name} = 0;")
|
|
166
|
+
self._w(f"dbg_prev_{group.name} = 0;")
|
|
167
|
+
for gate in self.analysis.singleton_gates:
|
|
168
|
+
self._w(f"s_{gate.name} = 0;")
|
|
169
|
+
self._w(f"dbg_prev_s_{gate.name} = 0;")
|
|
170
|
+
self._dedent()
|
|
171
|
+
self._w("}")
|
|
172
|
+
self._w()
|
|
173
|
+
|
|
174
|
+
def _emit_step_debug(self):
|
|
175
|
+
self._w("void step(int cycles) {")
|
|
176
|
+
self._indent()
|
|
177
|
+
self._w("for (int i = 0; i < cycles; ++i) {")
|
|
178
|
+
self._indent()
|
|
179
|
+
|
|
180
|
+
# Save previous state for breakpoint detection
|
|
181
|
+
self._w("dut.previous = dut.current;")
|
|
182
|
+
for group in self._group_list:
|
|
183
|
+
self._w(f"dbg_prev_{group.name} = {group.name};")
|
|
184
|
+
for gate in self.analysis.singleton_gates:
|
|
185
|
+
self._w(f"dbg_prev_s_{gate.name} = s_{gate.name};")
|
|
186
|
+
|
|
187
|
+
self._w("tick();")
|
|
188
|
+
self._w("dut.cycle_count++;")
|
|
189
|
+
self._dedent()
|
|
190
|
+
self._w("}")
|
|
191
|
+
self._w("dut.outputs_valid = 1;")
|
|
192
|
+
self._dedent()
|
|
193
|
+
self._w("}")
|
|
194
|
+
self._w()
|
|
195
|
+
|
|
196
|
+
# ── Debug API functions ──
|
|
197
|
+
|
|
198
|
+
def _emit_debug_api(self):
|
|
199
|
+
self._emit_get_cycle()
|
|
200
|
+
self._emit_peek_gate()
|
|
201
|
+
self._emit_peek_gate_previous()
|
|
202
|
+
self._emit_get_num_gates()
|
|
203
|
+
|
|
204
|
+
def _emit_get_cycle(self):
|
|
205
|
+
self._w("uint64_t get_cycle(void) {")
|
|
206
|
+
self._indent()
|
|
207
|
+
self._w("return dut.cycle_count;")
|
|
208
|
+
self._dedent()
|
|
209
|
+
self._w("}")
|
|
210
|
+
self._w()
|
|
211
|
+
|
|
212
|
+
def _emit_peek_gate(self):
|
|
213
|
+
self._w("uint64_t peek_gate(const char *gate_name) {")
|
|
214
|
+
self._indent()
|
|
215
|
+
|
|
216
|
+
# Ensure circuit is evaluated
|
|
217
|
+
self._w("if (!dut.outputs_valid) {")
|
|
218
|
+
self._indent()
|
|
219
|
+
self._w("tick();")
|
|
220
|
+
self._w("dut.outputs_valid = 1;")
|
|
221
|
+
self._dedent()
|
|
222
|
+
self._w("}")
|
|
223
|
+
self._w()
|
|
224
|
+
|
|
225
|
+
self._w("for (size_t i = 0; i < NUM_GATES; i++) {")
|
|
226
|
+
self._indent()
|
|
227
|
+
self._w("if (strcmp(GATE_TABLE[i].name, gate_name) == 0) {")
|
|
228
|
+
self._indent()
|
|
229
|
+
|
|
230
|
+
self._w("if (GATE_TABLE[i].is_group) {")
|
|
231
|
+
self._indent()
|
|
232
|
+
self._w("uint64_t val;")
|
|
233
|
+
self._emit_group_switch("val", is_prev=False)
|
|
234
|
+
self._w("return (val >> GATE_TABLE[i].bit_pos) & 1ull;")
|
|
235
|
+
self._dedent()
|
|
236
|
+
|
|
237
|
+
self._w("} else {")
|
|
238
|
+
self._indent()
|
|
239
|
+
self._emit_singleton_switch(is_prev=False)
|
|
240
|
+
self._dedent()
|
|
241
|
+
self._w("}")
|
|
242
|
+
|
|
243
|
+
self._dedent()
|
|
244
|
+
self._w("}")
|
|
245
|
+
self._dedent()
|
|
246
|
+
self._w("}")
|
|
247
|
+
self._w()
|
|
248
|
+
self._w("return 0ull;")
|
|
249
|
+
self._dedent()
|
|
250
|
+
self._w("}")
|
|
251
|
+
self._w()
|
|
252
|
+
|
|
253
|
+
def _emit_peek_gate_previous(self):
|
|
254
|
+
self._w("uint64_t peek_gate_previous(const char *gate_name) {")
|
|
255
|
+
self._indent()
|
|
256
|
+
|
|
257
|
+
self._w("for (size_t i = 0; i < NUM_GATES; i++) {")
|
|
258
|
+
self._indent()
|
|
259
|
+
self._w("if (strcmp(GATE_TABLE[i].name, gate_name) == 0) {")
|
|
260
|
+
self._indent()
|
|
261
|
+
|
|
262
|
+
self._w("if (GATE_TABLE[i].is_group) {")
|
|
263
|
+
self._indent()
|
|
264
|
+
self._w("uint64_t val;")
|
|
265
|
+
self._emit_group_switch("val", is_prev=True)
|
|
266
|
+
self._w("return (val >> GATE_TABLE[i].bit_pos) & 1ull;")
|
|
267
|
+
self._dedent()
|
|
268
|
+
|
|
269
|
+
self._w("} else {")
|
|
270
|
+
self._indent()
|
|
271
|
+
self._emit_singleton_switch(is_prev=True)
|
|
272
|
+
self._dedent()
|
|
273
|
+
self._w("}")
|
|
274
|
+
|
|
275
|
+
self._dedent()
|
|
276
|
+
self._w("}")
|
|
277
|
+
self._dedent()
|
|
278
|
+
self._w("}")
|
|
279
|
+
self._w()
|
|
280
|
+
self._w("return 0ull;")
|
|
281
|
+
self._dedent()
|
|
282
|
+
self._w("}")
|
|
283
|
+
self._w()
|
|
284
|
+
|
|
285
|
+
def _emit_get_num_gates(self):
|
|
286
|
+
self._w("size_t get_num_gates(void) {")
|
|
287
|
+
self._indent()
|
|
288
|
+
self._w("return NUM_GATES;")
|
|
289
|
+
self._dedent()
|
|
290
|
+
self._w("}")
|
|
291
|
+
self._w()
|
|
292
|
+
|
|
293
|
+
# ── Helpers for peek_gate switch statements ──
|
|
294
|
+
|
|
295
|
+
def _emit_group_switch(self, target_var: str, is_prev: bool):
|
|
296
|
+
prefix = "dbg_prev_" if is_prev else ""
|
|
297
|
+
self._w("switch (GATE_TABLE[i].idx) {")
|
|
298
|
+
self._indent()
|
|
299
|
+
for idx, group in enumerate(self._group_list):
|
|
300
|
+
self._w(f"case {idx}: {target_var} = {prefix}{group.name}; break;")
|
|
301
|
+
self._w(f"default: {target_var} = 0; break;")
|
|
302
|
+
self._dedent()
|
|
303
|
+
self._w("}")
|
|
304
|
+
|
|
305
|
+
def _emit_singleton_switch(self, is_prev: bool):
|
|
306
|
+
prefix = "dbg_prev_s_" if is_prev else "s_"
|
|
307
|
+
self._w("switch (GATE_TABLE[i].idx) {")
|
|
308
|
+
self._indent()
|
|
309
|
+
for idx, gate in enumerate(self.analysis.singleton_gates):
|
|
310
|
+
self._w(f"case {idx}: return (uint64_t){prefix}{gate.name};")
|
|
311
|
+
self._w("default: return 0ull;")
|
|
312
|
+
self._dedent()
|
|
313
|
+
self._w("}")
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Debug Info Generator for Bus Compiler.
|
|
3
|
+
|
|
4
|
+
Generates .shdb debug info files from bus compiler AnalysisResult,
|
|
5
|
+
matching the format expected by the debugger (DebugInfo).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
from .analyzer import AnalysisResult
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BusDebugInfoBuilder:
|
|
15
|
+
"""Builds .shdb debug info from bus compiler analysis."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, analysis: AnalysisResult, source_file: str = ""):
|
|
18
|
+
self.analysis = analysis
|
|
19
|
+
self.source_file = source_file
|
|
20
|
+
self.component_name = ""
|
|
21
|
+
|
|
22
|
+
def set_component_name(self, name: str) -> "BusDebugInfoBuilder":
|
|
23
|
+
self.component_name = name
|
|
24
|
+
return self
|
|
25
|
+
|
|
26
|
+
def build(self) -> dict:
|
|
27
|
+
"""Build the debug info dictionary."""
|
|
28
|
+
gates = {}
|
|
29
|
+
|
|
30
|
+
# Gates from bus groups
|
|
31
|
+
for group in self.analysis.bus_groups:
|
|
32
|
+
for pos, gate in enumerate(group.gates):
|
|
33
|
+
gates[gate.name] = {
|
|
34
|
+
"type": gate.primitive,
|
|
35
|
+
"lane": pos,
|
|
36
|
+
"chunk": 0, # Bus compiler doesn't use chunk/lane packing
|
|
37
|
+
"hierarchy_path": self._infer_hierarchy(gate.name),
|
|
38
|
+
"original_name": self._extract_original(gate.name),
|
|
39
|
+
"parent_instance": self._extract_parent(gate.name),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Singleton gates
|
|
43
|
+
for gate in self.analysis.singleton_gates:
|
|
44
|
+
gates[gate.name] = {
|
|
45
|
+
"type": gate.primitive,
|
|
46
|
+
"lane": 0,
|
|
47
|
+
"chunk": 0,
|
|
48
|
+
"hierarchy_path": self._infer_hierarchy(gate.name),
|
|
49
|
+
"original_name": self._extract_original(gate.name),
|
|
50
|
+
"parent_instance": self._extract_parent(gate.name),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
"version": "1.0",
|
|
55
|
+
"component": self.component_name,
|
|
56
|
+
"source_file": self.source_file,
|
|
57
|
+
"ports": {
|
|
58
|
+
"inputs": [
|
|
59
|
+
{"name": name, "width": width, "source_line": 0, "source_column": 0}
|
|
60
|
+
for name, width in self.analysis.input_ports.items()
|
|
61
|
+
],
|
|
62
|
+
"outputs": [
|
|
63
|
+
{"name": name, "width": width, "source_line": 0, "source_column": 0}
|
|
64
|
+
for name, width in self.analysis.output_ports.items()
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
"hierarchy": {},
|
|
68
|
+
"gates": gates,
|
|
69
|
+
"connections": [],
|
|
70
|
+
"constants": {},
|
|
71
|
+
"source_map": {},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
def save(self, path: str) -> None:
|
|
75
|
+
"""Save to a .shdb file."""
|
|
76
|
+
data = self.build()
|
|
77
|
+
with open(path, "w") as f:
|
|
78
|
+
json.dump(data, f, indent=2)
|
|
79
|
+
|
|
80
|
+
def _infer_hierarchy(self, gate_name: str) -> str:
|
|
81
|
+
if "_" in gate_name:
|
|
82
|
+
parts = gate_name.split("_")
|
|
83
|
+
return f"{self.component_name}/" + "/".join(parts)
|
|
84
|
+
return f"{self.component_name}/{gate_name}"
|
|
85
|
+
|
|
86
|
+
def _extract_original(self, gate_name: str) -> str:
|
|
87
|
+
if "_" in gate_name:
|
|
88
|
+
return gate_name.split("_")[-1]
|
|
89
|
+
return gate_name
|
|
90
|
+
|
|
91
|
+
def _extract_parent(self, gate_name: str) -> str:
|
|
92
|
+
if "_" in gate_name:
|
|
93
|
+
parts = gate_name.split("_")
|
|
94
|
+
if len(parts) > 1:
|
|
95
|
+
return "_".join(parts[:-1])
|
|
96
|
+
return ""
|