chipgraph 0.1.0__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.
- chipgraph/__init__.py +7 -0
- chipgraph/adapters/__init__.py +1 -0
- chipgraph/adapters/format/__init__.py +6 -0
- chipgraph/adapters/format/__main__.py +16 -0
- chipgraph/adapters/format/_yaml_lines.py +54 -0
- chipgraph/adapters/format/chip_yaml.py +323 -0
- chipgraph/adapters/format/qsoc_contract.py +246 -0
- chipgraph/adapters/llm/__init__.py +58 -0
- chipgraph/adapters/llm/_budget.py +173 -0
- chipgraph/adapters/llm/_errors.py +50 -0
- chipgraph/adapters/llm/_nda.py +30 -0
- chipgraph/adapters/llm/_retry.py +85 -0
- chipgraph/adapters/llm/anthropic.py +247 -0
- chipgraph/adapters/llm/decide_backend.py +179 -0
- chipgraph/adapters/llm/fake.py +87 -0
- chipgraph/adapters/llm/guard.py +68 -0
- chipgraph/adapters/parser/__init__.py +22 -0
- chipgraph/adapters/parser/cocotb.py +304 -0
- chipgraph/adapters/parser/generic_regex.py +75 -0
- chipgraph/adapters/parser/verible.py +55 -0
- chipgraph/adapters/parser/verilator.py +46 -0
- chipgraph/adapters/review/__init__.py +7 -0
- chipgraph/adapters/review/file.py +123 -0
- chipgraph/adapters/review/github.py +886 -0
- chipgraph/adapters/runner/__init__.py +7 -0
- chipgraph/adapters/runner/local.py +122 -0
- chipgraph/adapters/runtime/__init__.py +5 -0
- chipgraph/adapters/runtime/claude_code/__init__.py +24 -0
- chipgraph/adapters/runtime/claude_code/agents.py +158 -0
- chipgraph/adapters/runtime/claude_code/decisions.py +280 -0
- chipgraph/adapters/runtime/claude_code/guarded.py +310 -0
- chipgraph/adapters/runtime/claude_code/isolation.py +482 -0
- chipgraph/adapters/runtime/claude_code/loop.py +342 -0
- chipgraph/adapters/runtime/claude_code/previous.py +419 -0
- chipgraph/adapters/runtime/claude_code/review.py +492 -0
- chipgraph/adapters/runtime/claude_code/runtime.py +192 -0
- chipgraph/adapters/runtime/claude_code/service.py +2285 -0
- chipgraph/adapters/tool/__init__.py +7 -0
- chipgraph/adapters/tool/cmd.py +229 -0
- chipgraph/adapters/tool/edalize.py +773 -0
- chipgraph/adapters/tool/filelist.py +259 -0
- chipgraph/adapters/tool/pyslang.py +485 -0
- chipgraph/adapters/vcs/__init__.py +1 -0
- chipgraph/adapters/vcs/git.py +521 -0
- chipgraph/app/__init__.py +10 -0
- chipgraph/app/baseline.py +339 -0
- chipgraph/app/build.py +332 -0
- chipgraph/app/checks.py +202 -0
- chipgraph/app/context.py +191 -0
- chipgraph/app/errors.py +11 -0
- chipgraph/app/executors.py +79 -0
- chipgraph/app/findings.py +203 -0
- chipgraph/app/ingest.py +523 -0
- chipgraph/checks/__init__.py +52 -0
- chipgraph/checks/_cdc_analyze.py +272 -0
- chipgraph/checks/_cdc_elaborate.py +455 -0
- chipgraph/checks/_common.py +268 -0
- chipgraph/checks/_cross.py +96 -0
- chipgraph/checks/_model.py +64 -0
- chipgraph/checks/_naming_pyslang.py +235 -0
- chipgraph/checks/_naming_rules.py +127 -0
- chipgraph/checks/_xref_syntax.py +260 -0
- chipgraph/checks/cdc_struct.py +235 -0
- chipgraph/checks/connect.py +353 -0
- chipgraph/checks/cross_chip.py +454 -0
- chipgraph/checks/duplicate.py +153 -0
- chipgraph/checks/filelist.py +323 -0
- chipgraph/checks/format.py +268 -0
- chipgraph/checks/generated.py +151 -0
- chipgraph/checks/hardcode.py +233 -0
- chipgraph/checks/layout.py +155 -0
- chipgraph/checks/naming.py +311 -0
- chipgraph/checks/plan_check.py +901 -0
- chipgraph/checks/ports_diff.py +203 -0
- chipgraph/checks/spec_schema.py +277 -0
- chipgraph/checks/style.py +910 -0
- chipgraph/checks/tb_static.py +599 -0
- chipgraph/checks/trace.py +224 -0
- chipgraph/checks/vendor_guard.py +369 -0
- chipgraph/checks/wrap_check.py +486 -0
- chipgraph/cli/__init__.py +1688 -0
- chipgraph/cli/next_steps.py +137 -0
- chipgraph/core/__init__.py +1 -0
- chipgraph/core/config/__init__.py +93 -0
- chipgraph/core/config/defaults.py +14 -0
- chipgraph/core/config/errors.py +32 -0
- chipgraph/core/config/loader.py +766 -0
- chipgraph/core/config/models.py +845 -0
- chipgraph/core/config/templates.py +340 -0
- chipgraph/core/contracts/__init__.py +75 -0
- chipgraph/core/contracts/_paths.py +26 -0
- chipgraph/core/contracts/agent.py +49 -0
- chipgraph/core/contracts/artifact.py +68 -0
- chipgraph/core/contracts/check.py +76 -0
- chipgraph/core/contracts/event.py +70 -0
- chipgraph/core/contracts/export.py +128 -0
- chipgraph/core/contracts/finding.py +171 -0
- chipgraph/core/contracts/gate.py +50 -0
- chipgraph/core/contracts/rule.py +177 -0
- chipgraph/core/contracts/types.py +45 -0
- chipgraph/core/engine/__init__.py +70 -0
- chipgraph/core/engine/agent_rule.py +1362 -0
- chipgraph/core/engine/baseline.py +283 -0
- chipgraph/core/engine/decide.py +462 -0
- chipgraph/core/engine/fanout.py +1358 -0
- chipgraph/core/engine/gate.py +170 -0
- chipgraph/core/engine/graph.py +647 -0
- chipgraph/core/engine/records.py +60 -0
- chipgraph/core/engine/rules.py +92 -0
- chipgraph/core/engine/scheduler.py +637 -0
- chipgraph/core/engine/select.py +1239 -0
- chipgraph/core/engine/targets.py +342 -0
- chipgraph/core/model/__init__.py +98 -0
- chipgraph/core/model/entities.py +244 -0
- chipgraph/core/model/export.py +16 -0
- chipgraph/core/model/ingest.py +693 -0
- chipgraph/core/model/json_value.py +4 -0
- chipgraph/core/model/keys.py +95 -0
- chipgraph/core/model/model.py +153 -0
- chipgraph/core/model/provenance.py +27 -0
- chipgraph/core/model/query.py +583 -0
- chipgraph/core/model/registry.py +80 -0
- chipgraph/core/model/relations.py +61 -0
- chipgraph/core/model/store.py +279 -0
- chipgraph/core/plugin_api/__init__.py +81 -0
- chipgraph/core/plugin_api/local.py +167 -0
- chipgraph/core/plugin_api/pack.py +159 -0
- chipgraph/core/plugin_api/protocols.py +177 -0
- chipgraph/core/plugin_api/registry.py +111 -0
- chipgraph/core/plugin_api/types.py +94 -0
- chipgraph/core/runtime/__init__.py +41 -0
- chipgraph/core/runtime/executor.py +79 -0
- chipgraph/core/runtime/queue.py +565 -0
- chipgraph/core/runtime/roles/__init__.py +91 -0
- chipgraph/core/runtime/roles/data/author.md +41 -0
- chipgraph/core/runtime/roles/data/critic.md +56 -0
- chipgraph/core/runtime/roles/data/planner.md +38 -0
- chipgraph/core/runtime/roles/data/researcher.md +37 -0
- chipgraph/core/runtime/roles/data/rtl-author.md +64 -0
- chipgraph/core/runtime/roles/data/tb-author.md +48 -0
- chipgraph/core/runtime/roles/data/triage.md +34 -0
- chipgraph/core/runtime/roles/policy.py +299 -0
- chipgraph/core/runtime/roles/registry.py +52 -0
- chipgraph/core/runtime/roles/review.py +275 -0
- chipgraph/core/runtime/roles/schema.py +82 -0
- chipgraph/core/runtime/roles/skills.py +134 -0
- chipgraph/core/runtime/roles/spec.py +232 -0
- chipgraph/core/state/__init__.py +1 -0
- chipgraph/core/state/artifacts.py +156 -0
- chipgraph/core/state/backend.py +117 -0
- chipgraph/core/state/findings.py +151 -0
- chipgraph/core/state/handoff.py +411 -0
- chipgraph/core/state/idempotency.py +59 -0
- chipgraph/core/state/journal.py +238 -0
- chipgraph/core/state/layout.py +82 -0
- chipgraph/core/state/lock.py +195 -0
- chipgraph/core/state/trace.py +225 -0
- chipgraph/learn/__init__.py +96 -0
- chipgraph/learn/draft.py +187 -0
- chipgraph/learn/infer.py +580 -0
- chipgraph/learn/models.py +145 -0
- chipgraph/learn/try_run.py +228 -0
- chipgraph/mcp/__init__.py +12 -0
- chipgraph/mcp/model_tools.py +167 -0
- chipgraph/mcp/server.py +787 -0
- chipgraph/orgs/qnsc/naming-v1.yml +113 -0
- chipgraph/orgs/qnsc/profile.yml +5 -0
- chipgraph/packs/__init__.py +8 -0
- chipgraph/packs/assist/__init__.py +16 -0
- chipgraph/packs/assist/ask/__init__.py +58 -0
- chipgraph/packs/assist/ask/_db.py +57 -0
- chipgraph/packs/assist/ask/_project.py +230 -0
- chipgraph/packs/assist/ask/answer.py +266 -0
- chipgraph/packs/assist/ask/check.py +182 -0
- chipgraph/packs/assist/ask/contract.py +115 -0
- chipgraph/packs/assist/ask/documents.py +157 -0
- chipgraph/packs/assist/ask/fts.py +80 -0
- chipgraph/packs/assist/ask/retrieve.py +331 -0
- chipgraph/packs/assist/audit/__init__.py +383 -0
- chipgraph/packs/assist/pack.yml +13 -0
- chipgraph/packs/assist/triage/__init__.py +64 -0
- chipgraph/packs/assist/triage/contract.py +94 -0
- chipgraph/packs/assist/triage/parse.py +371 -0
- chipgraph/packs/assist/triage/question.py +384 -0
- chipgraph/packs/assist/triage/report.py +160 -0
- chipgraph/packs/assist/triage/rules.py +447 -0
- chipgraph/packs/assist/triage/run.py +224 -0
- chipgraph/packs/digital_rtl/__init__.py +19 -0
- chipgraph/packs/digital_rtl/layout.py +144 -0
- chipgraph/packs/digital_rtl/pack.yml +25 -0
- chipgraph/packs/digital_rtl/plan/__init__.py +34 -0
- chipgraph/packs/digital_rtl/plan/__main__.py +16 -0
- chipgraph/packs/digital_rtl/plan/context.py +82 -0
- chipgraph/packs/digital_rtl/plan/expand.py +94 -0
- chipgraph/packs/digital_rtl/plan/model.py +150 -0
- chipgraph/packs/digital_rtl/plan/resolver.py +400 -0
- chipgraph/packs/digital_rtl/plan/show.py +176 -0
- chipgraph/packs/digital_rtl/pr/__init__.py +11 -0
- chipgraph/packs/digital_rtl/pr/body.py +165 -0
- chipgraph/packs/digital_rtl/pr/gate.py +110 -0
- chipgraph/packs/digital_rtl/pr/open.py +397 -0
- chipgraph/packs/digital_rtl/rtl/__init__.py +9 -0
- chipgraph/packs/digital_rtl/rtl/context.py +272 -0
- chipgraph/packs/digital_rtl/rules/open_pr.yml +30 -0
- chipgraph/packs/digital_rtl/rules/plan.yml +28 -0
- chipgraph/packs/digital_rtl/rules/plan_expand.yml +29 -0
- chipgraph/packs/digital_rtl/rules/pr_gate.yml +27 -0
- chipgraph/packs/digital_rtl/rules/review.yml +31 -0
- chipgraph/packs/digital_rtl/rules/rtl_module.yml +53 -0
- chipgraph/packs/digital_rtl/skills/lang-sv-rtl.md +58 -0
- chipgraph/packs/digital_rtl/skills/org-naming.md +29 -0
- chipgraph/packs/digital_rtl/skills/plan-modules.md +63 -0
- chipgraph/packs/digital_rtl/skills/review-diff.md +41 -0
- chipgraph/packs/digital_rtl/targets/plan.yml +11 -0
- chipgraph/packs/digital_rtl/targets/rtl.yml +20 -0
- chipgraph/packs/digital_rtl/wrapper/__init__.py +24 -0
- chipgraph/packs/digital_rtl/wrapper/common.py +171 -0
- chipgraph/packs/digital_rtl/wrapper/context.py +222 -0
- chipgraph/packs/digital_rtl/wrapper/emacs.py +626 -0
- chipgraph/packs/digital_rtl/wrapper/expansion.py +304 -0
- chipgraph/packs/digital_rtl/wrapper/lisp.py +504 -0
- chipgraph/packs/digital_rtl/wrapper/resolver.py +60 -0
- chipgraph/packs/digital_rtl/wrapper/rules/wrapper.yml +41 -0
- chipgraph/packs/digital_rtl/wrapper/rules/wrapper_gen.yml +35 -0
- chipgraph/packs/digital_rtl/wrapper/skills/wrapper-auto.md +120 -0
- chipgraph/packs/dv/__init__.py +15 -0
- chipgraph/packs/dv/declaration.py +392 -0
- chipgraph/packs/dv/interface.py +596 -0
- chipgraph/packs/dv/pack.yml +14 -0
- chipgraph/packs/dv/rules/tb_module.yml +42 -0
- chipgraph/packs/dv/skills/dv/cocotb.md +72 -0
- chipgraph/packs/spec_core/__init__.py +9 -0
- chipgraph/packs/spec_core/extract/__init__.py +12 -0
- chipgraph/packs/spec_core/extract/_markdown.py +371 -0
- chipgraph/packs/spec_core/extract/_text.py +116 -0
- chipgraph/packs/spec_core/extract/mas.py +975 -0
- chipgraph/packs/spec_core/gen/__init__.py +11 -0
- chipgraph/packs/spec_core/gen/agents_md/__init__.py +148 -0
- chipgraph/packs/spec_core/gen/agents_md/_context.py +239 -0
- chipgraph/packs/spec_core/gen/agents_md/_markers.py +131 -0
- chipgraph/packs/spec_core/gen/agents_md/agents_md.md.j2 +178 -0
- chipgraph/packs/spec_core/gen/diagram/__init__.py +110 -0
- chipgraph/packs/spec_core/gen/diagram/__main__.py +44 -0
- chipgraph/packs/spec_core/gen/diagram/_diagrams.py +403 -0
- chipgraph/packs/spec_core/gen/diagram/_scene.py +163 -0
- chipgraph/packs/spec_core/pack.yml +6 -0
- chipgraph/py.typed +0 -0
- chipgraph-0.1.0.dist-info/METADATA +53 -0
- chipgraph-0.1.0.dist-info/RECORD +252 -0
- chipgraph-0.1.0.dist-info/WHEEL +4 -0
- chipgraph-0.1.0.dist-info/entry_points.txt +61 -0
- chipgraph-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""Build the four Design Model figures as deterministic :class:`Scene` objects.
|
|
2
|
+
|
|
3
|
+
Each builder reads only from the typed Design Model, sorts everything by model key (or by
|
|
4
|
+
address, for the memory map), and lays elements out on a fixed integer grid so the SVG and
|
|
5
|
+
drawio renderers produce byte-identical output for a given model. Nothing here draws a
|
|
6
|
+
figure by hand or calls an AI: the picture is a function of the model (DESIGN.md 4.6).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from chipgraph.core.model import DesignModel, EntityBase
|
|
12
|
+
from chipgraph.packs.spec_core.gen.diagram._scene import Box, Edge, Scene, escape_id
|
|
13
|
+
|
|
14
|
+
_MARGIN = 20
|
|
15
|
+
_COL_W = 180
|
|
16
|
+
_ROW_H = 44
|
|
17
|
+
_ROW_GAP = 12
|
|
18
|
+
_COL_GAP = 60
|
|
19
|
+
_EMPTY_W = 360
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _empty_scene(title: str, kind_desc: str) -> Scene:
|
|
23
|
+
"""A valid figure that says there is no data, for an empty or partial model."""
|
|
24
|
+
box = Box(id="no_data", x=_MARGIN, y=_MARGIN, w=_EMPTY_W, h=_ROW_H, text="no data")
|
|
25
|
+
return Scene(
|
|
26
|
+
title=title,
|
|
27
|
+
desc=f"{kind_desc}: no data in the model.",
|
|
28
|
+
width=_EMPTY_W + 2 * _MARGIN,
|
|
29
|
+
height=_ROW_H + 2 * _MARGIN,
|
|
30
|
+
boxes=(box,),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _name_of(entity: EntityBase) -> str:
|
|
35
|
+
return entity.name
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _attr_str(entity: EntityBase, key: str) -> str | None:
|
|
39
|
+
value = entity.attrs.get(key)
|
|
40
|
+
return value if isinstance(value, str) else None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _attr_names(entity: EntityBase, key: str) -> tuple[str, ...]:
|
|
44
|
+
value = entity.attrs.get(key)
|
|
45
|
+
if not isinstance(value, list):
|
|
46
|
+
return ()
|
|
47
|
+
return tuple(str(item) for item in value)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _block_name(model: DesignModel, key: object) -> str:
|
|
51
|
+
"""The name of the block at `key`, or ``-`` when it is missing/unset."""
|
|
52
|
+
if not isinstance(key, str):
|
|
53
|
+
return "-"
|
|
54
|
+
entity = model.get(key)
|
|
55
|
+
return entity.name if entity is not None else "-"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def block_diagram(model: DesignModel) -> Scene:
|
|
59
|
+
"""Buses (interfaces) with their masters and slave blocks; lone blocks still shown."""
|
|
60
|
+
title = "Block diagram"
|
|
61
|
+
interfaces = sorted(model.by_kind("interface"), key=lambda e: e.key)
|
|
62
|
+
blocks = {e.key: e for e in model.by_kind("block")}
|
|
63
|
+
if not interfaces and not blocks:
|
|
64
|
+
return _empty_scene(title, "Block diagram")
|
|
65
|
+
|
|
66
|
+
# Which block keys are reached by a bus, so lone blocks can be listed separately.
|
|
67
|
+
connected: set[str] = set()
|
|
68
|
+
boxes: list[Box] = []
|
|
69
|
+
edges: list[Edge] = []
|
|
70
|
+
y = _MARGIN
|
|
71
|
+
bus_x = _MARGIN
|
|
72
|
+
slave_x = _MARGIN + _COL_W + _COL_GAP
|
|
73
|
+
max_x = slave_x + _COL_W
|
|
74
|
+
|
|
75
|
+
for interface in interfaces:
|
|
76
|
+
masters = _attr_names(interface, "masters")
|
|
77
|
+
bus_id = escape_id(interface.key)
|
|
78
|
+
proto_value = getattr(interface, "protocol", None)
|
|
79
|
+
proto = proto_value if isinstance(proto_value, str) else ""
|
|
80
|
+
header = f"bus: {interface.name}"
|
|
81
|
+
if proto:
|
|
82
|
+
header += f"\n{proto}"
|
|
83
|
+
if masters:
|
|
84
|
+
header += "\nmasters: " + ", ".join(sorted(masters))
|
|
85
|
+
bus_y = y
|
|
86
|
+
boxes.append(Box(id=bus_id, x=bus_x, y=bus_y, w=_COL_W, h=_ROW_H, text=header))
|
|
87
|
+
slaves = sorted(
|
|
88
|
+
model.get_relations(src=interface.key, kind="connects"), key=lambda r: r.dst
|
|
89
|
+
)
|
|
90
|
+
row_y = y
|
|
91
|
+
for relation in slaves:
|
|
92
|
+
block = blocks.get(relation.dst)
|
|
93
|
+
if block is None:
|
|
94
|
+
continue
|
|
95
|
+
connected.add(block.key)
|
|
96
|
+
block_id = escape_id(block.key)
|
|
97
|
+
boxes.append(Box(id=block_id, x=slave_x, y=row_y, w=_COL_W, h=_ROW_H, text=block.name))
|
|
98
|
+
edges.append(Edge(id=f"{bus_id}__{block_id}", src=bus_id, dst=block_id))
|
|
99
|
+
row_y += _ROW_H + _ROW_GAP
|
|
100
|
+
y = max(row_y, bus_y + _ROW_H + _ROW_GAP)
|
|
101
|
+
|
|
102
|
+
lone = sorted((b for k, b in blocks.items() if k not in connected), key=lambda e: e.key)
|
|
103
|
+
if lone:
|
|
104
|
+
boxes.append(Box(id="lone_header", x=_MARGIN, y=y, w=_COL_W, h=_ROW_H, text="not on a bus"))
|
|
105
|
+
row_y = y
|
|
106
|
+
for block in lone:
|
|
107
|
+
boxes.append(
|
|
108
|
+
Box(
|
|
109
|
+
id=escape_id(block.key),
|
|
110
|
+
x=slave_x,
|
|
111
|
+
y=row_y,
|
|
112
|
+
w=_COL_W,
|
|
113
|
+
h=_ROW_H,
|
|
114
|
+
text=block.name,
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
row_y += _ROW_H + _ROW_GAP
|
|
118
|
+
y = max(row_y, y + _ROW_H + _ROW_GAP)
|
|
119
|
+
|
|
120
|
+
height = max(y, _MARGIN + _ROW_H) + _MARGIN
|
|
121
|
+
return Scene(
|
|
122
|
+
title=title,
|
|
123
|
+
desc=(
|
|
124
|
+
f"Block diagram: {len(interfaces)} bus(es), {len(blocks)} block(s), "
|
|
125
|
+
f"{len(lone)} block(s) not on a bus."
|
|
126
|
+
),
|
|
127
|
+
width=max_x + _MARGIN,
|
|
128
|
+
height=height,
|
|
129
|
+
boxes=tuple(boxes),
|
|
130
|
+
edges=tuple(edges),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _region_base(entity: EntityBase) -> int | None:
|
|
135
|
+
base = getattr(entity, "base", None)
|
|
136
|
+
return base if isinstance(base, int) else None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _region_size(entity: EntityBase) -> int | None:
|
|
140
|
+
size = getattr(entity, "size", None)
|
|
141
|
+
return size if isinstance(size, int) else None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _hex_or(value: int | None, fallback: object) -> str:
|
|
145
|
+
if value is not None:
|
|
146
|
+
return f"{value:#010x}"
|
|
147
|
+
return "auto" if fallback == "auto" else str(fallback) if fallback is not None else "?"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _overlapping_keys(regions: list[EntityBase]) -> set[str]:
|
|
151
|
+
"""Model keys of regions that overlap another region (both endpoints marked)."""
|
|
152
|
+
placed = [
|
|
153
|
+
(e.key, base, size)
|
|
154
|
+
for e in regions
|
|
155
|
+
if (base := _region_base(e)) is not None and (size := _region_size(e)) is not None
|
|
156
|
+
]
|
|
157
|
+
placed.sort(key=lambda t: (t[1], t[0]))
|
|
158
|
+
bad: set[str] = set()
|
|
159
|
+
for i, (key_a, base_a, size_a) in enumerate(placed):
|
|
160
|
+
for key_b, base_b, _size_b in placed[i + 1 :]:
|
|
161
|
+
if base_b >= base_a + size_a:
|
|
162
|
+
break
|
|
163
|
+
bad.add(key_a)
|
|
164
|
+
bad.add(key_b)
|
|
165
|
+
return bad
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def memory_map(model: DesignModel) -> Scene:
|
|
169
|
+
"""Regions sorted by base; base/end/size in hex; overlaps outlined in the marker colour."""
|
|
170
|
+
title = "Memory map"
|
|
171
|
+
regions = model.by_kind("memory_region")
|
|
172
|
+
if not regions:
|
|
173
|
+
return _empty_scene(title, "Memory map")
|
|
174
|
+
|
|
175
|
+
# Sort by base (numeric first, in address order), then by key for stability.
|
|
176
|
+
def sort_key(entity: EntityBase) -> tuple[int, int, str]:
|
|
177
|
+
base = _region_base(entity)
|
|
178
|
+
if base is None:
|
|
179
|
+
return (1, 0, entity.key)
|
|
180
|
+
return (0, base, entity.key)
|
|
181
|
+
|
|
182
|
+
ordered = sorted(regions, key=sort_key)
|
|
183
|
+
bad = _overlapping_keys(list(regions))
|
|
184
|
+
|
|
185
|
+
boxes: list[Box] = []
|
|
186
|
+
width = 520
|
|
187
|
+
y = _MARGIN
|
|
188
|
+
header = Box(
|
|
189
|
+
id="mm_header",
|
|
190
|
+
x=_MARGIN,
|
|
191
|
+
y=y,
|
|
192
|
+
w=width,
|
|
193
|
+
h=_ROW_H,
|
|
194
|
+
text="Memory map (base / end / size / block)",
|
|
195
|
+
)
|
|
196
|
+
boxes.append(header)
|
|
197
|
+
y += _ROW_H + _ROW_GAP
|
|
198
|
+
for entity in ordered:
|
|
199
|
+
base = _region_base(entity)
|
|
200
|
+
size = _region_size(entity)
|
|
201
|
+
end = base + size if base is not None and size is not None else None
|
|
202
|
+
base_s = _hex_or(base, getattr(entity, "base", None))
|
|
203
|
+
end_s = _hex_or(end, None) if end is not None else "?"
|
|
204
|
+
size_s = _hex_or(size, getattr(entity, "size", None))
|
|
205
|
+
block = getattr(entity, "block", None)
|
|
206
|
+
block_name = _block_name(model, block)
|
|
207
|
+
text = f"{entity.name}\n{base_s} .. {end_s} size {size_s} block {block_name}"
|
|
208
|
+
overlap = entity.key in bad
|
|
209
|
+
if overlap:
|
|
210
|
+
text += "\n[OVERLAP]"
|
|
211
|
+
boxes.append(
|
|
212
|
+
Box(
|
|
213
|
+
id=escape_id(entity.key),
|
|
214
|
+
x=_MARGIN,
|
|
215
|
+
y=y,
|
|
216
|
+
w=width,
|
|
217
|
+
h=_ROW_H + (16 if overlap else 0),
|
|
218
|
+
text=text,
|
|
219
|
+
overlap=overlap,
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
y += _ROW_H + (16 if overlap else 0) + _ROW_GAP
|
|
223
|
+
|
|
224
|
+
desc = f"Memory map: {len(regions)} region(s)"
|
|
225
|
+
if bad:
|
|
226
|
+
desc += f", {len(bad)} in an overlap (marked)"
|
|
227
|
+
desc += "."
|
|
228
|
+
return Scene(
|
|
229
|
+
title=title,
|
|
230
|
+
desc=desc,
|
|
231
|
+
width=width + 2 * _MARGIN,
|
|
232
|
+
height=y + _MARGIN,
|
|
233
|
+
boxes=tuple(boxes),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def interrupt_map(model: DesignModel) -> Scene:
|
|
238
|
+
"""Line number -> source (name, block); interrupts without a line/block are unassigned."""
|
|
239
|
+
title = "Interrupt map"
|
|
240
|
+
interrupts = model.by_kind("interrupt")
|
|
241
|
+
if not interrupts:
|
|
242
|
+
return _empty_scene(title, "Interrupt map")
|
|
243
|
+
|
|
244
|
+
def line_of(entity: EntityBase) -> int | None:
|
|
245
|
+
line = getattr(entity, "line", None)
|
|
246
|
+
return line if isinstance(line, int) else None
|
|
247
|
+
|
|
248
|
+
assigned = sorted(
|
|
249
|
+
(e for e in interrupts if line_of(e) is not None and getattr(e, "block", None)),
|
|
250
|
+
key=lambda e: (line_of(e), e.key),
|
|
251
|
+
)
|
|
252
|
+
unassigned = sorted(
|
|
253
|
+
(e for e in interrupts if line_of(e) is None or not getattr(e, "block", None)),
|
|
254
|
+
key=lambda e: e.key,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
boxes: list[Box] = []
|
|
258
|
+
width = 460
|
|
259
|
+
y = _MARGIN
|
|
260
|
+
boxes.append(
|
|
261
|
+
Box(
|
|
262
|
+
id="irq_header",
|
|
263
|
+
x=_MARGIN,
|
|
264
|
+
y=y,
|
|
265
|
+
w=width,
|
|
266
|
+
h=_ROW_H,
|
|
267
|
+
text="Interrupt map (line -> source)",
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
y += _ROW_H + _ROW_GAP
|
|
271
|
+
# Two sources on one line are bad data: mark them like overlapping memory regions.
|
|
272
|
+
line_counts: dict[int, int] = {}
|
|
273
|
+
for entity in interrupts:
|
|
274
|
+
number = line_of(entity)
|
|
275
|
+
if number is not None:
|
|
276
|
+
line_counts[number] = line_counts.get(number, 0) + 1
|
|
277
|
+
for entity in assigned:
|
|
278
|
+
line = line_of(entity)
|
|
279
|
+
block = getattr(entity, "block", None)
|
|
280
|
+
block_name = _block_name(model, block)
|
|
281
|
+
shared = line is not None and line_counts.get(line, 0) > 1
|
|
282
|
+
text = f"line {line}: {entity.name} (block {block_name})"
|
|
283
|
+
if shared:
|
|
284
|
+
text += " [line shared]"
|
|
285
|
+
boxes.append(
|
|
286
|
+
Box(
|
|
287
|
+
id=escape_id(entity.key),
|
|
288
|
+
x=_MARGIN,
|
|
289
|
+
y=y,
|
|
290
|
+
w=width,
|
|
291
|
+
h=_ROW_H,
|
|
292
|
+
text=text,
|
|
293
|
+
overlap=shared,
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
y += _ROW_H + _ROW_GAP
|
|
297
|
+
if unassigned:
|
|
298
|
+
boxes.append(
|
|
299
|
+
Box(
|
|
300
|
+
id="irq_unassigned",
|
|
301
|
+
x=_MARGIN,
|
|
302
|
+
y=y,
|
|
303
|
+
w=width,
|
|
304
|
+
h=_ROW_H,
|
|
305
|
+
text="unassigned (no line, or no block in the model)",
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
y += _ROW_H + _ROW_GAP
|
|
309
|
+
for entity in unassigned:
|
|
310
|
+
block = getattr(entity, "block", None)
|
|
311
|
+
block_name = _block_name(model, block)
|
|
312
|
+
line = line_of(entity)
|
|
313
|
+
line_s = str(line) if line is not None else "-"
|
|
314
|
+
shared = line is not None and line_counts.get(line, 0) > 1
|
|
315
|
+
text = f"{entity.name} (line {line_s}, block {block_name})"
|
|
316
|
+
if shared:
|
|
317
|
+
text += " [line shared]"
|
|
318
|
+
boxes.append(
|
|
319
|
+
Box(
|
|
320
|
+
id=escape_id(entity.key),
|
|
321
|
+
x=_MARGIN,
|
|
322
|
+
y=y,
|
|
323
|
+
w=width,
|
|
324
|
+
h=_ROW_H,
|
|
325
|
+
text=text,
|
|
326
|
+
overlap=shared,
|
|
327
|
+
)
|
|
328
|
+
)
|
|
329
|
+
y += _ROW_H + _ROW_GAP
|
|
330
|
+
|
|
331
|
+
return Scene(
|
|
332
|
+
title=title,
|
|
333
|
+
desc=(f"Interrupt map: {len(assigned)} assigned line(s), {len(unassigned)} unassigned."),
|
|
334
|
+
width=width + 2 * _MARGIN,
|
|
335
|
+
height=y + _MARGIN,
|
|
336
|
+
boxes=tuple(boxes),
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _blocks_using(model: DesignModel, node: EntityBase, block_attr: str) -> list[EntityBase]:
|
|
341
|
+
"""Blocks that use a clock/reset `node`, via block.attrs[attr] or node.attrs['blocks']."""
|
|
342
|
+
blocks = {e.key: e for e in model.by_kind("block")}
|
|
343
|
+
found: dict[str, EntityBase] = {}
|
|
344
|
+
for block in blocks.values():
|
|
345
|
+
if _attr_str(block, block_attr) == node.name:
|
|
346
|
+
found[block.key] = block
|
|
347
|
+
for name in _attr_names(node, "blocks"):
|
|
348
|
+
for block in blocks.values():
|
|
349
|
+
if block.name == name:
|
|
350
|
+
found[block.key] = block
|
|
351
|
+
return sorted(found.values(), key=lambda e: e.key)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def clock_reset_tree(model: DesignModel) -> Scene:
|
|
355
|
+
"""Each clock and reset -> the blocks that use it."""
|
|
356
|
+
title = "Clock/reset tree"
|
|
357
|
+
clocks = sorted(model.by_kind("clock"), key=lambda e: e.key)
|
|
358
|
+
resets = sorted(model.by_kind("reset"), key=lambda e: e.key)
|
|
359
|
+
if not clocks and not resets:
|
|
360
|
+
return _empty_scene(title, "Clock/reset tree")
|
|
361
|
+
|
|
362
|
+
boxes: list[Box] = []
|
|
363
|
+
edges: list[Edge] = []
|
|
364
|
+
node_x = _MARGIN
|
|
365
|
+
block_x = _MARGIN + _COL_W + _COL_GAP
|
|
366
|
+
max_x = block_x + _COL_W
|
|
367
|
+
y = _MARGIN
|
|
368
|
+
n_edges = 0
|
|
369
|
+
|
|
370
|
+
for node, kind_label, attr in (
|
|
371
|
+
*((c, "clock", "clock") for c in clocks),
|
|
372
|
+
*((r, "reset", "reset") for r in resets),
|
|
373
|
+
):
|
|
374
|
+
node_id = escape_id(node.key)
|
|
375
|
+
node_y = y
|
|
376
|
+
boxes.append(
|
|
377
|
+
Box(
|
|
378
|
+
id=node_id,
|
|
379
|
+
x=node_x,
|
|
380
|
+
y=node_y,
|
|
381
|
+
w=_COL_W,
|
|
382
|
+
h=_ROW_H,
|
|
383
|
+
text=f"{kind_label}: {node.name}",
|
|
384
|
+
)
|
|
385
|
+
)
|
|
386
|
+
users = _blocks_using(model, node, attr)
|
|
387
|
+
row_y = y
|
|
388
|
+
for block in users:
|
|
389
|
+
block_id = f"{node_id}__use__{escape_id(block.key)}"
|
|
390
|
+
boxes.append(Box(id=block_id, x=block_x, y=row_y, w=_COL_W, h=_ROW_H, text=block.name))
|
|
391
|
+
edges.append(Edge(id=f"e{n_edges}", src=node_id, dst=block_id))
|
|
392
|
+
n_edges += 1
|
|
393
|
+
row_y += _ROW_H + _ROW_GAP
|
|
394
|
+
y = max(row_y, node_y + _ROW_H + _ROW_GAP)
|
|
395
|
+
|
|
396
|
+
return Scene(
|
|
397
|
+
title=title,
|
|
398
|
+
desc=f"Clock/reset tree: {len(clocks)} clock(s), {len(resets)} reset(s).",
|
|
399
|
+
width=max_x + _MARGIN,
|
|
400
|
+
height=max(y, _MARGIN + _ROW_H) + _MARGIN,
|
|
401
|
+
boxes=tuple(boxes),
|
|
402
|
+
edges=tuple(edges),
|
|
403
|
+
)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""A tiny deterministic scene model and its SVG / drawio renderers.
|
|
2
|
+
|
|
3
|
+
A :class:`Scene` is a title, a description and an ordered list of :class:`Box`,
|
|
4
|
+
:class:`Edge` and :class:`Row` elements laid out on a fixed integer grid. Both renderers
|
|
5
|
+
(:func:`scene_to_svg`, :func:`scene_to_drawio`) walk the same scene, so the two files
|
|
6
|
+
always agree, and both are byte-deterministic: elements keep the order the diagram code
|
|
7
|
+
put them in, ids come from escaped model keys, coordinates are integers, text is XML
|
|
8
|
+
escaped, and there are no timestamps, random ids or attributes that change between runs.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
|
|
15
|
+
FONT_SIZE = 12
|
|
16
|
+
"""The single readable font size used for all element text."""
|
|
17
|
+
|
|
18
|
+
_OVERLAP_STROKE = "#cc0000"
|
|
19
|
+
"""The one non-monochrome colour, reserved for the memory-map overlap marker."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True, slots=True)
|
|
23
|
+
class Box:
|
|
24
|
+
"""A rectangle with text, placed by its top-left corner on the grid."""
|
|
25
|
+
|
|
26
|
+
id: str
|
|
27
|
+
x: int
|
|
28
|
+
y: int
|
|
29
|
+
w: int
|
|
30
|
+
h: int
|
|
31
|
+
text: str
|
|
32
|
+
overlap: bool = False
|
|
33
|
+
"""When true, the box is outlined in the overlap colour (bad-data marker)."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True, slots=True)
|
|
37
|
+
class Edge:
|
|
38
|
+
"""A directed connection between two boxes, drawn as an arrow."""
|
|
39
|
+
|
|
40
|
+
id: str
|
|
41
|
+
src: str
|
|
42
|
+
dst: str
|
|
43
|
+
label: str = ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True, slots=True)
|
|
47
|
+
class Scene:
|
|
48
|
+
"""A whole figure: metadata plus its boxes and edges, in draw order."""
|
|
49
|
+
|
|
50
|
+
title: str
|
|
51
|
+
desc: str
|
|
52
|
+
width: int
|
|
53
|
+
height: int
|
|
54
|
+
boxes: tuple[Box, ...] = field(default_factory=tuple)
|
|
55
|
+
edges: tuple[Edge, ...] = field(default_factory=tuple)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def xml_escape(text: str) -> str:
|
|
59
|
+
"""Escape a string for use in XML text and double-quoted attribute values."""
|
|
60
|
+
return (
|
|
61
|
+
text.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def escape_id(key: str) -> str:
|
|
66
|
+
"""Turn a model key into a stable id token (letters, digits, ``_`` only)."""
|
|
67
|
+
return "".join(c if c.isalnum() else "_" for c in key)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _box_center(box: Box) -> tuple[int, int]:
|
|
71
|
+
return box.x + box.w // 2, box.y + box.h // 2
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def scene_to_svg(scene: Scene) -> bytes:
|
|
75
|
+
"""Render `scene` to a self-contained, accessible SVG document (UTF-8, ``\\n``)."""
|
|
76
|
+
lines: list[str] = []
|
|
77
|
+
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
|
78
|
+
lines.append(
|
|
79
|
+
f'<svg xmlns="http://www.w3.org/2000/svg" '
|
|
80
|
+
f'width="{scene.width}" height="{scene.height}" '
|
|
81
|
+
f'viewBox="0 0 {scene.width} {scene.height}" '
|
|
82
|
+
f'font-family="sans-serif" font-size="{FONT_SIZE}">'
|
|
83
|
+
)
|
|
84
|
+
lines.append(f" <title>{xml_escape(scene.title)}</title>")
|
|
85
|
+
lines.append(f" <desc>{xml_escape(scene.desc)}</desc>")
|
|
86
|
+
lines.append(
|
|
87
|
+
' <defs><marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="3" '
|
|
88
|
+
'orient="auto"><path d="M0,0 L7,3 L0,6 z" fill="#000000"/></marker></defs>'
|
|
89
|
+
)
|
|
90
|
+
by_id = {box.id: box for box in scene.boxes}
|
|
91
|
+
for edge in scene.edges:
|
|
92
|
+
src, dst = by_id.get(edge.src), by_id.get(edge.dst)
|
|
93
|
+
if src is None or dst is None:
|
|
94
|
+
continue
|
|
95
|
+
x1, y1 = _box_center(src)
|
|
96
|
+
x2, y2 = _box_center(dst)
|
|
97
|
+
lines.append(
|
|
98
|
+
f' <line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" '
|
|
99
|
+
f'stroke="#000000" marker-end="url(#arrow)"/>'
|
|
100
|
+
)
|
|
101
|
+
if edge.label:
|
|
102
|
+
lx, ly = (x1 + x2) // 2, (y1 + y2) // 2
|
|
103
|
+
lines.append(
|
|
104
|
+
f' <text x="{lx}" y="{ly}" text-anchor="middle">{xml_escape(edge.label)}</text>'
|
|
105
|
+
)
|
|
106
|
+
for box in scene.boxes:
|
|
107
|
+
stroke = _OVERLAP_STROKE if box.overlap else "#000000"
|
|
108
|
+
lines.append(
|
|
109
|
+
f' <rect x="{box.x}" y="{box.y}" width="{box.w}" height="{box.h}" '
|
|
110
|
+
f'fill="#ffffff" stroke="{stroke}"/>'
|
|
111
|
+
)
|
|
112
|
+
cx = box.x + box.w // 2
|
|
113
|
+
for i, part in enumerate(box.text.split("\n")):
|
|
114
|
+
ty = box.y + FONT_SIZE + 4 + i * (FONT_SIZE + 4)
|
|
115
|
+
lines.append(
|
|
116
|
+
f' <text x="{cx}" y="{ty}" text-anchor="middle">{xml_escape(part)}</text>'
|
|
117
|
+
)
|
|
118
|
+
lines.append("</svg>")
|
|
119
|
+
return ("\n".join(lines) + "\n").encode("utf-8")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def scene_to_drawio(scene: Scene) -> bytes:
|
|
123
|
+
"""Render `scene` to an uncompressed drawio ``<mxfile>`` document (UTF-8, ``\\n``).
|
|
124
|
+
|
|
125
|
+
The ``<mxfile>`` has no ``modified``/``etag``/``agent``/``host`` attributes, so two
|
|
126
|
+
runs of the same scene produce byte-identical files.
|
|
127
|
+
"""
|
|
128
|
+
lines: list[str] = []
|
|
129
|
+
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
|
130
|
+
lines.append(f'<mxfile><diagram id="0" name="{xml_escape(scene.title)}">')
|
|
131
|
+
lines.append(
|
|
132
|
+
f'<mxGraphModel dx="{scene.width}" dy="{scene.height}" grid="0" '
|
|
133
|
+
f'pageWidth="{scene.width}" pageHeight="{scene.height}" math="0" shadow="0">'
|
|
134
|
+
)
|
|
135
|
+
lines.append("<root>")
|
|
136
|
+
lines.append('<mxCell id="0"/>')
|
|
137
|
+
lines.append('<mxCell id="1" parent="0"/>')
|
|
138
|
+
for box in scene.boxes:
|
|
139
|
+
stroke = _OVERLAP_STROKE if box.overlap else "#000000"
|
|
140
|
+
style = (
|
|
141
|
+
f"rounded=0;whiteSpace=wrap;html=0;fillColor=#ffffff;"
|
|
142
|
+
f"strokeColor={stroke};fontColor=#000000;fontSize={FONT_SIZE}"
|
|
143
|
+
)
|
|
144
|
+
lines.append(
|
|
145
|
+
f'<mxCell id="{box.id}" value="{xml_escape(box.text)}" '
|
|
146
|
+
f'style="{style}" vertex="1" parent="1">'
|
|
147
|
+
)
|
|
148
|
+
lines.append(
|
|
149
|
+
f'<mxGeometry x="{box.x}" y="{box.y}" width="{box.w}" height="{box.h}" as="geometry"/>'
|
|
150
|
+
)
|
|
151
|
+
lines.append("</mxCell>")
|
|
152
|
+
for edge in scene.edges:
|
|
153
|
+
style = "endArrow=classic;html=0;strokeColor=#000000;fontColor=#000000"
|
|
154
|
+
lines.append(
|
|
155
|
+
f'<mxCell id="{edge.id}" value="{xml_escape(edge.label)}" style="{style}" '
|
|
156
|
+
f'edge="1" parent="1" source="{edge.src}" target="{edge.dst}">'
|
|
157
|
+
)
|
|
158
|
+
lines.append('<mxGeometry relative="1" as="geometry"/>')
|
|
159
|
+
lines.append("</mxCell>")
|
|
160
|
+
lines.append("</root>")
|
|
161
|
+
lines.append("</mxGraphModel>")
|
|
162
|
+
lines.append("</diagram></mxfile>")
|
|
163
|
+
return ("\n".join(lines) + "\n").encode("utf-8")
|
chipgraph/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: chipgraph
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AI agent system for chip (IC) design, built on a build graph over a Design Model
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Requires-Dist: jinja2>=3.1
|
|
9
|
+
Requires-Dist: mcp<3,>=2.2
|
|
10
|
+
Requires-Dist: opentelemetry-api>=1.45
|
|
11
|
+
Requires-Dist: pydantic>=2.13
|
|
12
|
+
Requires-Dist: pyslang<13,>=12
|
|
13
|
+
Requires-Dist: pyyaml
|
|
14
|
+
Requires-Dist: typer>=0.27
|
|
15
|
+
Provides-Extra: evals
|
|
16
|
+
Requires-Dist: inspect-ai==0.3.285; extra == 'evals'
|
|
17
|
+
Provides-Extra: llm
|
|
18
|
+
Requires-Dist: anthropic<3,>=2.3; extra == 'llm'
|
|
19
|
+
Provides-Extra: otel
|
|
20
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.45; extra == 'otel'
|
|
21
|
+
Requires-Dist: opentelemetry-sdk>=1.45; extra == 'otel'
|
|
22
|
+
Provides-Extra: sim
|
|
23
|
+
Requires-Dist: cocotb==2.1.0; extra == 'sim'
|
|
24
|
+
Requires-Dist: edalize==0.6.8; extra == 'sim'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# chipgraph
|
|
28
|
+
|
|
29
|
+
chipgraph is an AI agent system for chip (IC) design. It treats a chip project as a
|
|
30
|
+
build graph: specs, RTL and tests are artifacts; rules produce them, and a rule can be
|
|
31
|
+
code, an AI agent or a human; deterministic EDA checks verify every step; humans approve
|
|
32
|
+
at gates. All of this runs over a typed Design Model of the chip.
|
|
33
|
+
|
|
34
|
+
Pre-alpha: the design is agreed (DESIGN.md) and implementation follows
|
|
35
|
+
docs/IMPLEMENTATION_PLAN.md.
|
|
36
|
+
|
|
37
|
+
## Development
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv sync
|
|
41
|
+
make check
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Learn more
|
|
45
|
+
|
|
46
|
+
- [`DESIGN.md`](DESIGN.md) — the design
|
|
47
|
+
- [`docs/DECISIONS.md`](docs/DECISIONS.md) — the reasons behind the design
|
|
48
|
+
- [`docs/IMPLEMENTATION_PLAN.md`](docs/IMPLEMENTATION_PLAN.md) — tasks, dependencies, acceptance criteria
|
|
49
|
+
- [`AGENTS.md`](AGENTS.md) — how to work on this repository
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
Apache-2.0
|