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,353 @@
|
|
|
1
|
+
"""`ConnectCheck`: the top's RTL wiring against the chip contract (layer 1).
|
|
2
|
+
|
|
3
|
+
A layer-1 (structure) cross check (DESIGN.md 4.8). It reads the Design Model
|
|
4
|
+
`chipgraph ingest` wrote and parses the top module's RTL file with the pyslang syntax
|
|
5
|
+
tree (no elaboration), collecting the top's hierarchy instances and their named port
|
|
6
|
+
connections, then compares them to the contract:
|
|
7
|
+
|
|
8
|
+
* `instance.missing` -- a contract block that should appear under the top (or a D38
|
|
9
|
+
`instance_of` instance) has no RTL instance of a module owned by its IP.
|
|
10
|
+
* `interrupt.wiring` -- a contract interrupt's output on its instance is not wired. The
|
|
11
|
+
interrupt output is the instance port whose *spec* name matches arg `irq_port`
|
|
12
|
+
(default `^o_int_|irq`). When the design declares an interrupt vector net (arg
|
|
13
|
+
`irq_vector`), the output must connect to bit `line` of that vector; when it does not
|
|
14
|
+
(as in the tinysoc example, whose single interrupt is a plain `timer_irq` output), the
|
|
15
|
+
rule is only that the interrupt output is connected to *something*.
|
|
16
|
+
|
|
17
|
+
Wiring the check cannot resolve (an expression it does not understand) is reported as
|
|
18
|
+
`info`, never guessed at. A missing model is a whole-check `error`; a top file that
|
|
19
|
+
cannot be found or parsed is an `error` too.
|
|
20
|
+
|
|
21
|
+
The top module is the RTL module of the `top` block (arg `top_file` overrides the file).
|
|
22
|
+
|
|
23
|
+
Example profile usage (`.chipgraph.yml`):
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
adapters:
|
|
27
|
+
connect: { use: connect, irq_port: "^o_int_|irq", irq_vector: "o_int_vec" }
|
|
28
|
+
```
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import re
|
|
34
|
+
import time
|
|
35
|
+
|
|
36
|
+
from chipgraph.checks._common import (
|
|
37
|
+
ArgError,
|
|
38
|
+
compute_idempotency_key,
|
|
39
|
+
error_result,
|
|
40
|
+
optional_str,
|
|
41
|
+
sha256_file,
|
|
42
|
+
)
|
|
43
|
+
from chipgraph.checks._model import ModelUnavailable, load_model
|
|
44
|
+
from chipgraph.checks._xref_syntax import Instance, parse_rtl
|
|
45
|
+
from chipgraph.core.contracts import CheckResult, CheckSpec, Issue
|
|
46
|
+
from chipgraph.core.contracts.types import CheckStatus
|
|
47
|
+
from chipgraph.core.model.entities import ModuleEntity
|
|
48
|
+
from chipgraph.core.model.model import DesignModel
|
|
49
|
+
from chipgraph.core.plugin_api.types import ToolContext
|
|
50
|
+
|
|
51
|
+
_DEFAULT_IRQ_PORT = r"^o_int_|irq"
|
|
52
|
+
_TOP_BLOCK = "top"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ConnectCheck:
|
|
56
|
+
"""Checks the top module's RTL instances and interrupt wiring against the contract."""
|
|
57
|
+
|
|
58
|
+
id = "connect"
|
|
59
|
+
name = "Connect"
|
|
60
|
+
per_block = False # chip-wide: `chipgraph check` runs it once, not per block
|
|
61
|
+
|
|
62
|
+
async def run(self, spec: CheckSpec, ctx: ToolContext) -> CheckResult:
|
|
63
|
+
start = time.monotonic()
|
|
64
|
+
args = spec.args
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
irq_port_pat = optional_str(args, "irq_port", _DEFAULT_IRQ_PORT)
|
|
68
|
+
irq_vector = optional_str(args, "irq_vector", "")
|
|
69
|
+
top_file_arg = optional_str(args, "top_file", "")
|
|
70
|
+
top_block = optional_str(args, "top_block", _TOP_BLOCK)
|
|
71
|
+
irq_port_re = re.compile(irq_port_pat)
|
|
72
|
+
except (ArgError, re.error) as exc:
|
|
73
|
+
return error_result(spec, str(exc), start)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
loaded = load_model(ctx.repo_root)
|
|
77
|
+
except ModelUnavailable as exc:
|
|
78
|
+
return error_result(spec, str(exc), start, rule="model")
|
|
79
|
+
|
|
80
|
+
model = loaded.model
|
|
81
|
+
top_rel = _top_file(model, top_block, top_file_arg)
|
|
82
|
+
if top_rel is None:
|
|
83
|
+
return error_result(
|
|
84
|
+
spec,
|
|
85
|
+
f"no top module file found for block {top_block!r}; set args['top_file']",
|
|
86
|
+
start,
|
|
87
|
+
rule="top",
|
|
88
|
+
)
|
|
89
|
+
top_path = ctx.repo_root / top_rel
|
|
90
|
+
if not top_path.is_file():
|
|
91
|
+
return error_result(spec, f"top file {top_rel!r} does not exist", start, rule="top")
|
|
92
|
+
|
|
93
|
+
parsed = parse_rtl(top_path)
|
|
94
|
+
issues: list[Issue] = [
|
|
95
|
+
Issue(
|
|
96
|
+
file=top_rel,
|
|
97
|
+
line=pf.line,
|
|
98
|
+
rule="parse",
|
|
99
|
+
severity="error",
|
|
100
|
+
msg=f"parse error: {pf.message}",
|
|
101
|
+
)
|
|
102
|
+
for pf in parsed.parse_errors
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
issues.extend(
|
|
106
|
+
_connect_issues(model, parsed.instances, top_rel, top_block, irq_port_re, irq_vector)
|
|
107
|
+
)
|
|
108
|
+
issues.sort(key=lambda i: (i.file or "", i.line or 0, i.rule, i.msg))
|
|
109
|
+
|
|
110
|
+
file_hashes = [
|
|
111
|
+
(top_rel, sha256_file(top_path)),
|
|
112
|
+
("<model>", loaded.build_inputs_hash or "none"),
|
|
113
|
+
]
|
|
114
|
+
key = compute_idempotency_key(spec.id, args, file_hashes)
|
|
115
|
+
status: CheckStatus = "fail" if any(i.severity == "error" for i in issues) else "pass"
|
|
116
|
+
return CheckResult(
|
|
117
|
+
check_id=spec.id,
|
|
118
|
+
status=status,
|
|
119
|
+
issues=tuple(issues),
|
|
120
|
+
duration_s=time.monotonic() - start,
|
|
121
|
+
idempotency_key=key,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _top_file(model: DesignModel, top_block: str, top_file_arg: str) -> str | None:
|
|
126
|
+
"""The repo-relative top RTL file: `top_file` arg wins, else the top block's module."""
|
|
127
|
+
if top_file_arg:
|
|
128
|
+
return top_file_arg
|
|
129
|
+
top_key = f"block:{top_block}"
|
|
130
|
+
modules = [
|
|
131
|
+
e
|
|
132
|
+
for e in model.by_kind("module")
|
|
133
|
+
if isinstance(e, ModuleEntity) and e.block == top_key and e.file
|
|
134
|
+
]
|
|
135
|
+
if not modules:
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
# The module of the top block with the most instances is the integration top.
|
|
139
|
+
def instance_count(module_key: str) -> int:
|
|
140
|
+
return len(model.get_relations(src=module_key, kind="instantiates"))
|
|
141
|
+
|
|
142
|
+
modules.sort(key=lambda m: (-instance_count(m.key), m.key))
|
|
143
|
+
return modules[0].file
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _connect_issues(
|
|
147
|
+
model: DesignModel,
|
|
148
|
+
instances: list[Instance],
|
|
149
|
+
top_rel: str,
|
|
150
|
+
top_block: str,
|
|
151
|
+
irq_port_re: re.Pattern[str],
|
|
152
|
+
irq_vector: str,
|
|
153
|
+
) -> list[Issue]:
|
|
154
|
+
issues: list[Issue] = []
|
|
155
|
+
instantiated_modules = {inst.module for inst in instances}
|
|
156
|
+
|
|
157
|
+
# instance.missing: every non-top contract block (and D38 instance) needs an RTL
|
|
158
|
+
# instance of a module its IP owns.
|
|
159
|
+
for block in sorted(model.by_kind("block"), key=lambda b: b.key):
|
|
160
|
+
name = block.name
|
|
161
|
+
if name == top_block:
|
|
162
|
+
continue
|
|
163
|
+
ip_key = _ip_key(model, block.key)
|
|
164
|
+
owned = _owned_module_names(model, ip_key)
|
|
165
|
+
if not owned:
|
|
166
|
+
# No RTL module is attributed to this IP yet; cannot check structurally.
|
|
167
|
+
issues.append(
|
|
168
|
+
Issue(
|
|
169
|
+
file=top_rel,
|
|
170
|
+
rule="instance.info",
|
|
171
|
+
severity="info",
|
|
172
|
+
msg=f"block {name!r} owns no RTL module in the model; instance not checked",
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
continue
|
|
176
|
+
if owned.isdisjoint(instantiated_modules):
|
|
177
|
+
issues.append(
|
|
178
|
+
Issue(
|
|
179
|
+
file=top_rel,
|
|
180
|
+
rule="instance.missing",
|
|
181
|
+
severity="error",
|
|
182
|
+
msg=(
|
|
183
|
+
f"contract block {name!r} has no RTL instance in the top of a module "
|
|
184
|
+
f"it owns (expected one of {sorted(owned)})"
|
|
185
|
+
),
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# interrupt.wiring: each contract interrupt output on its instance must be wired.
|
|
190
|
+
issues.extend(_interrupt_issues(model, instances, top_rel, irq_port_re, irq_vector))
|
|
191
|
+
return issues
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _ip_key(model: DesignModel, block_key: str) -> str:
|
|
195
|
+
"""The IP block key a block-as-instance points at via `instance_of`, else itself (D38)."""
|
|
196
|
+
rels = model.get_relations(src=block_key, kind="instance_of")
|
|
197
|
+
return rels[0].dst if rels else block_key
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _owned_module_names(model: DesignModel, block_key: str) -> set[str]:
|
|
201
|
+
"""The RTL module names owned by `block_key` (its `block` field)."""
|
|
202
|
+
return {
|
|
203
|
+
e.name
|
|
204
|
+
for e in model.by_kind("module")
|
|
205
|
+
if isinstance(e, ModuleEntity) and e.block == block_key
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _interrupt_issues(
|
|
210
|
+
model: DesignModel,
|
|
211
|
+
instances: list[Instance],
|
|
212
|
+
top_rel: str,
|
|
213
|
+
irq_port_re: re.Pattern[str],
|
|
214
|
+
irq_vector: str,
|
|
215
|
+
) -> list[Issue]:
|
|
216
|
+
issues: list[Issue] = []
|
|
217
|
+
for irq in sorted(model.by_kind("interrupt"), key=lambda e: e.key):
|
|
218
|
+
block_key = getattr(irq, "block", None)
|
|
219
|
+
if not isinstance(block_key, str):
|
|
220
|
+
continue
|
|
221
|
+
ip_key = block_key # interrupts live on the IP block itself
|
|
222
|
+
owned = _owned_module_names(model, ip_key)
|
|
223
|
+
irq_ports = _spec_irq_ports(model, block_key, irq_port_re)
|
|
224
|
+
if not irq_ports:
|
|
225
|
+
issues.append(
|
|
226
|
+
Issue(
|
|
227
|
+
file=top_rel,
|
|
228
|
+
rule="interrupt.info",
|
|
229
|
+
severity="info",
|
|
230
|
+
msg=(
|
|
231
|
+
f"interrupt {irq.name!r} of block {ip_key!r} has no spec port matching "
|
|
232
|
+
"the interrupt-port pattern; cannot check its wiring"
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
continue
|
|
237
|
+
inst = _instance_of(instances, owned)
|
|
238
|
+
if inst is None:
|
|
239
|
+
# instance.missing already covers a wholly absent instance.
|
|
240
|
+
continue
|
|
241
|
+
conn = None
|
|
242
|
+
matched_port = None
|
|
243
|
+
for port in irq_ports:
|
|
244
|
+
conn = inst.connection(port)
|
|
245
|
+
if conn is not None:
|
|
246
|
+
matched_port = port
|
|
247
|
+
break
|
|
248
|
+
if conn is None:
|
|
249
|
+
issues.append(
|
|
250
|
+
Issue(
|
|
251
|
+
file=top_rel,
|
|
252
|
+
line=inst.line,
|
|
253
|
+
rule="interrupt.wiring",
|
|
254
|
+
severity="error",
|
|
255
|
+
msg=(
|
|
256
|
+
f"interrupt output {sorted(irq_ports)} of instance {inst.name!r} "
|
|
257
|
+
f"(interrupt {irq.name!r}) is not connected by name in the top"
|
|
258
|
+
),
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
continue
|
|
262
|
+
expr = conn.expr.strip()
|
|
263
|
+
if not expr:
|
|
264
|
+
issues.append(
|
|
265
|
+
Issue(
|
|
266
|
+
file=top_rel,
|
|
267
|
+
line=conn.line,
|
|
268
|
+
rule="interrupt.wiring",
|
|
269
|
+
severity="error",
|
|
270
|
+
msg=(
|
|
271
|
+
f"interrupt output {matched_port!r} of instance {inst.name!r} "
|
|
272
|
+
f"(interrupt {irq.name!r}) is connected to nothing"
|
|
273
|
+
),
|
|
274
|
+
)
|
|
275
|
+
)
|
|
276
|
+
continue
|
|
277
|
+
if irq_vector:
|
|
278
|
+
issues.extend(
|
|
279
|
+
_vector_bit_issue(irq, inst, matched_port or "", conn, top_rel, irq_vector)
|
|
280
|
+
)
|
|
281
|
+
return issues
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _vector_bit_issue(
|
|
285
|
+
irq: object, inst: Instance, port: str, conn: object, top_rel: str, irq_vector: str
|
|
286
|
+
) -> list[Issue]:
|
|
287
|
+
"""Check the interrupt output connects to `irq_vector[line]`; `info` when unresolved."""
|
|
288
|
+
line_no = getattr(irq, "line", None)
|
|
289
|
+
expr = getattr(conn, "expr", "").strip()
|
|
290
|
+
conn_line = getattr(conn, "line", None)
|
|
291
|
+
if not isinstance(line_no, int):
|
|
292
|
+
return [
|
|
293
|
+
Issue(
|
|
294
|
+
file=top_rel,
|
|
295
|
+
line=conn_line,
|
|
296
|
+
rule="interrupt.info",
|
|
297
|
+
severity="info",
|
|
298
|
+
msg=f"interrupt {getattr(irq, 'name', '?')!r} has no line number; not checked",
|
|
299
|
+
)
|
|
300
|
+
]
|
|
301
|
+
m = re.fullmatch(rf"{re.escape(irq_vector)}\s*\[\s*(\d+)\s*\]", expr)
|
|
302
|
+
if m is None:
|
|
303
|
+
return [
|
|
304
|
+
Issue(
|
|
305
|
+
file=top_rel,
|
|
306
|
+
line=conn_line,
|
|
307
|
+
rule="interrupt.info",
|
|
308
|
+
severity="info",
|
|
309
|
+
msg=(
|
|
310
|
+
f"interrupt output {port!r} of {inst.name!r} connects to {expr!r}, "
|
|
311
|
+
f"which is not a resolvable bit of {irq_vector!r}; not checked further"
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
]
|
|
315
|
+
if int(m.group(1)) != line_no:
|
|
316
|
+
return [
|
|
317
|
+
Issue(
|
|
318
|
+
file=top_rel,
|
|
319
|
+
line=conn_line,
|
|
320
|
+
rule="interrupt.wiring",
|
|
321
|
+
severity="error",
|
|
322
|
+
msg=(
|
|
323
|
+
f"interrupt {getattr(irq, 'name', '?')!r} is line {line_no} in the contract "
|
|
324
|
+
f"but wired to {irq_vector}[{m.group(1)}]"
|
|
325
|
+
),
|
|
326
|
+
)
|
|
327
|
+
]
|
|
328
|
+
return []
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _spec_irq_ports(model: DesignModel, block_key: str, irq_port_re: re.Pattern[str]) -> set[str]:
|
|
332
|
+
"""Spec output ports of `block_key` whose name matches the interrupt-port pattern."""
|
|
333
|
+
out: set[str] = set()
|
|
334
|
+
for port in model.by_kind("port"):
|
|
335
|
+
if port.attrs.get("block") != block_key:
|
|
336
|
+
continue
|
|
337
|
+
if port.attrs.get("origin") != "spec":
|
|
338
|
+
continue
|
|
339
|
+
if getattr(port, "direction", None) != "output":
|
|
340
|
+
continue
|
|
341
|
+
if irq_port_re.search(port.name):
|
|
342
|
+
out.add(port.name)
|
|
343
|
+
return out
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _instance_of(instances: list[Instance], module_names: set[str]) -> Instance | None:
|
|
347
|
+
for inst in instances:
|
|
348
|
+
if inst.module in module_names:
|
|
349
|
+
return inst
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
__all__ = ["ConnectCheck"]
|