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,304 @@
|
|
|
1
|
+
"""The expanded wrapper read back, and checked against the IP's port declaration.
|
|
2
|
+
|
|
3
|
+
verilog-mode writes an instance's connections one per line after `/*AUTOINST*/`, each
|
|
4
|
+
`.<ip port> (<signal>),` and, when an `AUTO_TEMPLATE` line made it, `// Templated`. A
|
|
5
|
+
port no template line matches is connected to a signal of its own name, and the
|
|
6
|
+
`AUTOINPUT`/`AUTOOUTPUT` groups then declare that signal as a wrapper port: the IP port
|
|
7
|
+
is left unmapped. `instances` reads the instances of a module from the text (comments and
|
|
8
|
+
strings masked, so a template's text in a comment is never taken for code); `validate`
|
|
9
|
+
checks each one against the IP module's declaration (`ModuleDeclaration`, header only):
|
|
10
|
+
|
|
11
|
+
- `wrap.no_instance`: the wrapper does not instantiate the IP module;
|
|
12
|
+
- `wrap.autoinst_empty`: an `/*AUTOINST*/` that expanded to no connection;
|
|
13
|
+
- `wrap.unknown_port`: a connection to a port the IP does not declare;
|
|
14
|
+
- `wrap.port_missing`: an IP port the instance does not connect;
|
|
15
|
+
- `wrap.untemplated`: an IP port no template line maps (see above);
|
|
16
|
+
- `wrap.input_open`: an IP input left open, `()`; an open output is a note
|
|
17
|
+
(`wrap.output_open`): Verilator's lint warns about it (`PINCONNECTEMPTY`).
|
|
18
|
+
|
|
19
|
+
Every issue carries the expanded connection's text: what the author, who cannot run
|
|
20
|
+
emacs, needs to fix the template.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import re
|
|
26
|
+
from collections.abc import Callable
|
|
27
|
+
from dataclasses import dataclass
|
|
28
|
+
|
|
29
|
+
from chipgraph.core.contracts import Issue
|
|
30
|
+
from chipgraph.core.contracts.types import Severity
|
|
31
|
+
from chipgraph.packs.dv.declaration import ModuleDeclaration
|
|
32
|
+
|
|
33
|
+
_IDENT = re.compile(r"[A-Za-z_][A-Za-z0-9_$]*")
|
|
34
|
+
_MODULE_RE = re.compile(r"\bmodule\s+(?:automatic\s+|static\s+)?([A-Za-z_][A-Za-z0-9_$]*)")
|
|
35
|
+
_CONNECTION_RE = re.compile(r"\.\s*([A-Za-z_][A-Za-z0-9_$]*)")
|
|
36
|
+
_AUTOINST_RE = re.compile(r"/\*\s*AUTOINST\b")
|
|
37
|
+
_TEMPLATED = "Templated"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def mask(text: str) -> str:
|
|
41
|
+
"""`text` with comments and string literals blanked (newlines kept, same length)."""
|
|
42
|
+
out = list(text)
|
|
43
|
+
i, n = 0, len(text)
|
|
44
|
+
while i < n:
|
|
45
|
+
two = text[i : i + 2]
|
|
46
|
+
if two == "//":
|
|
47
|
+
j = text.find("\n", i)
|
|
48
|
+
j = n if j < 0 else j
|
|
49
|
+
elif two == "/*":
|
|
50
|
+
j = text.find("*/", i + 2)
|
|
51
|
+
j = n if j < 0 else j + 2
|
|
52
|
+
elif text[i] == '"':
|
|
53
|
+
j = i + 1
|
|
54
|
+
while j < n and text[j] not in '"\n':
|
|
55
|
+
j += 2 if text[j] == "\\" else 1
|
|
56
|
+
j = min(j + 1, n)
|
|
57
|
+
else:
|
|
58
|
+
i += 1
|
|
59
|
+
continue
|
|
60
|
+
for k in range(i, j):
|
|
61
|
+
if out[k] != "\n":
|
|
62
|
+
out[k] = " "
|
|
63
|
+
i = j
|
|
64
|
+
return "".join(out)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _close(code: str, open_at: int) -> int:
|
|
68
|
+
"""The index of the `)` that closes the `(` at `open_at` in masked `code`, or -1."""
|
|
69
|
+
depth = 0
|
|
70
|
+
for i in range(open_at, len(code)):
|
|
71
|
+
if code[i] == "(":
|
|
72
|
+
depth += 1
|
|
73
|
+
elif code[i] == ")":
|
|
74
|
+
depth -= 1
|
|
75
|
+
if depth == 0:
|
|
76
|
+
return i
|
|
77
|
+
return -1
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _skip_ws(code: str, i: int) -> int:
|
|
81
|
+
while i < len(code) and code[i].isspace():
|
|
82
|
+
i += 1
|
|
83
|
+
return i
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def line_of(text: str, offset: int) -> int:
|
|
87
|
+
"""The 1-based line of `offset` in `text`."""
|
|
88
|
+
return text.count("\n", 0, offset) + 1
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class Connection:
|
|
93
|
+
"""One named port connection of an instance."""
|
|
94
|
+
|
|
95
|
+
port: str
|
|
96
|
+
expr: str
|
|
97
|
+
"""The connected expression, as written (comments removed); '' when open."""
|
|
98
|
+
line: int
|
|
99
|
+
text: str
|
|
100
|
+
"""The connection's line, stripped: what the author is shown."""
|
|
101
|
+
templated: bool
|
|
102
|
+
manual: bool
|
|
103
|
+
"""Written before `/*AUTOINST*/` in the source, i.e. by hand."""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(frozen=True)
|
|
107
|
+
class Instance:
|
|
108
|
+
"""One instance of a module in a wrapper."""
|
|
109
|
+
|
|
110
|
+
module: str
|
|
111
|
+
name: str
|
|
112
|
+
line: int
|
|
113
|
+
autoinst: bool
|
|
114
|
+
connections: tuple[Connection, ...]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def module_name(text: str) -> str | None:
|
|
118
|
+
"""The name of the first module the text declares."""
|
|
119
|
+
match = _MODULE_RE.search(mask(text))
|
|
120
|
+
return match.group(1) if match else None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def instances(text: str, module: str) -> list[Instance]:
|
|
124
|
+
"""Every instance of `module` in `text` (comments and strings never count)."""
|
|
125
|
+
code = mask(text)
|
|
126
|
+
found: list[Instance] = []
|
|
127
|
+
for match in re.finditer(rf"(?<![\w$`.]){re.escape(module)}(?![\w$])", code):
|
|
128
|
+
i = _skip_ws(code, match.end())
|
|
129
|
+
if i < len(code) and code[i] == "#":
|
|
130
|
+
i = _skip_ws(code, i + 1)
|
|
131
|
+
if i >= len(code) or code[i] != "(":
|
|
132
|
+
continue
|
|
133
|
+
end = _close(code, i)
|
|
134
|
+
if end < 0:
|
|
135
|
+
continue
|
|
136
|
+
i = _skip_ws(code, end + 1)
|
|
137
|
+
ident = _IDENT.match(code, i)
|
|
138
|
+
if ident is None or ident.group(0) in ("module", "endmodule"):
|
|
139
|
+
continue
|
|
140
|
+
name = ident.group(0)
|
|
141
|
+
i = _skip_ws(code, ident.end())
|
|
142
|
+
while i < len(code) and code[i] == "[":
|
|
143
|
+
close = code.find("]", i)
|
|
144
|
+
if close < 0:
|
|
145
|
+
break
|
|
146
|
+
i = _skip_ws(code, close + 1)
|
|
147
|
+
if i >= len(code) or code[i] != "(":
|
|
148
|
+
continue
|
|
149
|
+
end = _close(code, i)
|
|
150
|
+
if end < 0:
|
|
151
|
+
continue
|
|
152
|
+
region = text[i + 1 : end]
|
|
153
|
+
auto = _AUTOINST_RE.search(region)
|
|
154
|
+
auto_at = i + 1 + auto.start() if auto else -1
|
|
155
|
+
found.append(
|
|
156
|
+
Instance(
|
|
157
|
+
module=module,
|
|
158
|
+
name=name,
|
|
159
|
+
line=line_of(text, match.start()),
|
|
160
|
+
autoinst=auto is not None,
|
|
161
|
+
connections=tuple(_connections(text, code, i + 1, end, auto_at)),
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
return found
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _connections(text: str, code: str, start: int, end: int, auto_at: int) -> list[Connection]:
|
|
168
|
+
out: list[Connection] = []
|
|
169
|
+
i = start
|
|
170
|
+
while True:
|
|
171
|
+
match = _CONNECTION_RE.search(code, i, end)
|
|
172
|
+
if match is None:
|
|
173
|
+
return out
|
|
174
|
+
port = match.group(1)
|
|
175
|
+
j = _skip_ws(code, match.end())
|
|
176
|
+
if j < end and code[j] == "(":
|
|
177
|
+
close = _close(code, j)
|
|
178
|
+
close = end if close < 0 or close > end else close
|
|
179
|
+
expr = " ".join(code[j + 1 : close].split())
|
|
180
|
+
after = close + 1
|
|
181
|
+
else: # `.name` (implicit) connects the signal of that name
|
|
182
|
+
expr, after = port, match.end()
|
|
183
|
+
line_end = text.find("\n", after)
|
|
184
|
+
line_end = len(text) if line_end < 0 else line_end
|
|
185
|
+
line_start = text.rfind("\n", 0, match.start()) + 1
|
|
186
|
+
line_text = text[line_start:line_end]
|
|
187
|
+
out.append(
|
|
188
|
+
Connection(
|
|
189
|
+
port=port,
|
|
190
|
+
expr=expr,
|
|
191
|
+
line=line_of(text, match.start()),
|
|
192
|
+
text=" ".join(line_text.split()),
|
|
193
|
+
templated=_TEMPLATED in text[after:line_end],
|
|
194
|
+
manual=auto_at >= 0 and match.start() < auto_at,
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
i = after
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def validate(
|
|
201
|
+
text: str,
|
|
202
|
+
declaration: ModuleDeclaration,
|
|
203
|
+
*,
|
|
204
|
+
file: str,
|
|
205
|
+
line_for: Callable[[Instance], int | None] | None = None,
|
|
206
|
+
wrapper: str | None = None,
|
|
207
|
+
) -> list[Issue]:
|
|
208
|
+
"""The problems of the expanded `text`'s instances of the IP (see the module).
|
|
209
|
+
|
|
210
|
+
Issues point at `file`, at the line `line_for(instance)` gives (default: the
|
|
211
|
+
instance's line in `text`).
|
|
212
|
+
"""
|
|
213
|
+
issues: list[Issue] = []
|
|
214
|
+
vendor = declaration.module
|
|
215
|
+
ports = {p.name: p for p in declaration.ports}
|
|
216
|
+
found = instances(text, vendor)
|
|
217
|
+
|
|
218
|
+
def issue(rule: str, msg: str, inst: Instance | None, severity: Severity = "error") -> None:
|
|
219
|
+
line = (line_for(inst) if line_for is not None else inst.line) if inst else None
|
|
220
|
+
issues.append(Issue(file=file, line=line, rule=rule, severity=severity, msg=msg))
|
|
221
|
+
|
|
222
|
+
declared = module_name(text)
|
|
223
|
+
if wrapper is not None and declared != wrapper:
|
|
224
|
+
issue(
|
|
225
|
+
"wrap.module_name",
|
|
226
|
+
f"the source declares module `{declared}`, not the wrapper `{wrapper}`",
|
|
227
|
+
None,
|
|
228
|
+
)
|
|
229
|
+
if not found:
|
|
230
|
+
issue(
|
|
231
|
+
"wrap.no_instance",
|
|
232
|
+
f"the wrapper does not instantiate the IP module `{vendor}`: write "
|
|
233
|
+
f"`{vendor} u_<name> (/*AUTOINST*/);`",
|
|
234
|
+
None,
|
|
235
|
+
)
|
|
236
|
+
for inst in found:
|
|
237
|
+
where = f"instance `{inst.name}` of `{vendor}`"
|
|
238
|
+
if inst.autoinst and not inst.connections:
|
|
239
|
+
issue(
|
|
240
|
+
"wrap.autoinst_empty",
|
|
241
|
+
f"/*AUTOINST*/ of {where} expanded to no port: emacs found no port of "
|
|
242
|
+
f"`{vendor}` (is the module name right, and its file in vendor_files?)",
|
|
243
|
+
inst,
|
|
244
|
+
)
|
|
245
|
+
continue
|
|
246
|
+
connected: set[str] = set()
|
|
247
|
+
for conn in inst.connections:
|
|
248
|
+
connected.add(conn.port)
|
|
249
|
+
port = ports.get(conn.port)
|
|
250
|
+
if port is None:
|
|
251
|
+
issue(
|
|
252
|
+
"wrap.unknown_port",
|
|
253
|
+
f"{where} connects `{conn.port}`, which `{vendor}` does not declare: "
|
|
254
|
+
f"`{conn.text}`",
|
|
255
|
+
inst,
|
|
256
|
+
)
|
|
257
|
+
continue
|
|
258
|
+
direction = port.direction or "port"
|
|
259
|
+
if not conn.expr:
|
|
260
|
+
if direction == "input":
|
|
261
|
+
issue(
|
|
262
|
+
"wrap.input_open",
|
|
263
|
+
f"{where} leaves the input `{conn.port}` unconnected: `{conn.text}`; "
|
|
264
|
+
"tie it (1'b0, '0) or map it in the AUTO_TEMPLATE",
|
|
265
|
+
inst,
|
|
266
|
+
)
|
|
267
|
+
else:
|
|
268
|
+
issue(
|
|
269
|
+
"wrap.output_open",
|
|
270
|
+
f"{where} leaves the {direction} `{conn.port}` open: `{conn.text}`; "
|
|
271
|
+
"lint warns about an empty connection, sink it in an `unused_*` "
|
|
272
|
+
"signal instead",
|
|
273
|
+
inst,
|
|
274
|
+
"info",
|
|
275
|
+
)
|
|
276
|
+
continue
|
|
277
|
+
if inst.autoinst and not conn.manual and not conn.templated:
|
|
278
|
+
issue(
|
|
279
|
+
"wrap.untemplated",
|
|
280
|
+
f"{where}: the {direction} `{conn.port}` is not mapped by the AUTO_TEMPLATE, "
|
|
281
|
+
f"so it is left unconnected to any project signal: the expansion wires it "
|
|
282
|
+
f"to a new wrapper port of the IP's own name: `{conn.text}`. Add a template "
|
|
283
|
+
f"line for `.{conn.port}` (or fix the regexp that should match it)",
|
|
284
|
+
inst,
|
|
285
|
+
)
|
|
286
|
+
for name in ports:
|
|
287
|
+
if name not in connected:
|
|
288
|
+
issue(
|
|
289
|
+
"wrap.port_missing",
|
|
290
|
+
f"{where} does not connect the {ports[name].direction or 'port'} `{name}`",
|
|
291
|
+
inst,
|
|
292
|
+
)
|
|
293
|
+
return issues
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
__all__ = [
|
|
297
|
+
"Connection",
|
|
298
|
+
"Instance",
|
|
299
|
+
"instances",
|
|
300
|
+
"line_of",
|
|
301
|
+
"mask",
|
|
302
|
+
"module_name",
|
|
303
|
+
"validate",
|
|
304
|
+
]
|