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.
Files changed (252) hide show
  1. chipgraph/__init__.py +7 -0
  2. chipgraph/adapters/__init__.py +1 -0
  3. chipgraph/adapters/format/__init__.py +6 -0
  4. chipgraph/adapters/format/__main__.py +16 -0
  5. chipgraph/adapters/format/_yaml_lines.py +54 -0
  6. chipgraph/adapters/format/chip_yaml.py +323 -0
  7. chipgraph/adapters/format/qsoc_contract.py +246 -0
  8. chipgraph/adapters/llm/__init__.py +58 -0
  9. chipgraph/adapters/llm/_budget.py +173 -0
  10. chipgraph/adapters/llm/_errors.py +50 -0
  11. chipgraph/adapters/llm/_nda.py +30 -0
  12. chipgraph/adapters/llm/_retry.py +85 -0
  13. chipgraph/adapters/llm/anthropic.py +247 -0
  14. chipgraph/adapters/llm/decide_backend.py +179 -0
  15. chipgraph/adapters/llm/fake.py +87 -0
  16. chipgraph/adapters/llm/guard.py +68 -0
  17. chipgraph/adapters/parser/__init__.py +22 -0
  18. chipgraph/adapters/parser/cocotb.py +304 -0
  19. chipgraph/adapters/parser/generic_regex.py +75 -0
  20. chipgraph/adapters/parser/verible.py +55 -0
  21. chipgraph/adapters/parser/verilator.py +46 -0
  22. chipgraph/adapters/review/__init__.py +7 -0
  23. chipgraph/adapters/review/file.py +123 -0
  24. chipgraph/adapters/review/github.py +886 -0
  25. chipgraph/adapters/runner/__init__.py +7 -0
  26. chipgraph/adapters/runner/local.py +122 -0
  27. chipgraph/adapters/runtime/__init__.py +5 -0
  28. chipgraph/adapters/runtime/claude_code/__init__.py +24 -0
  29. chipgraph/adapters/runtime/claude_code/agents.py +158 -0
  30. chipgraph/adapters/runtime/claude_code/decisions.py +280 -0
  31. chipgraph/adapters/runtime/claude_code/guarded.py +310 -0
  32. chipgraph/adapters/runtime/claude_code/isolation.py +482 -0
  33. chipgraph/adapters/runtime/claude_code/loop.py +342 -0
  34. chipgraph/adapters/runtime/claude_code/previous.py +419 -0
  35. chipgraph/adapters/runtime/claude_code/review.py +492 -0
  36. chipgraph/adapters/runtime/claude_code/runtime.py +192 -0
  37. chipgraph/adapters/runtime/claude_code/service.py +2285 -0
  38. chipgraph/adapters/tool/__init__.py +7 -0
  39. chipgraph/adapters/tool/cmd.py +229 -0
  40. chipgraph/adapters/tool/edalize.py +773 -0
  41. chipgraph/adapters/tool/filelist.py +259 -0
  42. chipgraph/adapters/tool/pyslang.py +485 -0
  43. chipgraph/adapters/vcs/__init__.py +1 -0
  44. chipgraph/adapters/vcs/git.py +521 -0
  45. chipgraph/app/__init__.py +10 -0
  46. chipgraph/app/baseline.py +339 -0
  47. chipgraph/app/build.py +332 -0
  48. chipgraph/app/checks.py +202 -0
  49. chipgraph/app/context.py +191 -0
  50. chipgraph/app/errors.py +11 -0
  51. chipgraph/app/executors.py +79 -0
  52. chipgraph/app/findings.py +203 -0
  53. chipgraph/app/ingest.py +523 -0
  54. chipgraph/checks/__init__.py +52 -0
  55. chipgraph/checks/_cdc_analyze.py +272 -0
  56. chipgraph/checks/_cdc_elaborate.py +455 -0
  57. chipgraph/checks/_common.py +268 -0
  58. chipgraph/checks/_cross.py +96 -0
  59. chipgraph/checks/_model.py +64 -0
  60. chipgraph/checks/_naming_pyslang.py +235 -0
  61. chipgraph/checks/_naming_rules.py +127 -0
  62. chipgraph/checks/_xref_syntax.py +260 -0
  63. chipgraph/checks/cdc_struct.py +235 -0
  64. chipgraph/checks/connect.py +353 -0
  65. chipgraph/checks/cross_chip.py +454 -0
  66. chipgraph/checks/duplicate.py +153 -0
  67. chipgraph/checks/filelist.py +323 -0
  68. chipgraph/checks/format.py +268 -0
  69. chipgraph/checks/generated.py +151 -0
  70. chipgraph/checks/hardcode.py +233 -0
  71. chipgraph/checks/layout.py +155 -0
  72. chipgraph/checks/naming.py +311 -0
  73. chipgraph/checks/plan_check.py +901 -0
  74. chipgraph/checks/ports_diff.py +203 -0
  75. chipgraph/checks/spec_schema.py +277 -0
  76. chipgraph/checks/style.py +910 -0
  77. chipgraph/checks/tb_static.py +599 -0
  78. chipgraph/checks/trace.py +224 -0
  79. chipgraph/checks/vendor_guard.py +369 -0
  80. chipgraph/checks/wrap_check.py +486 -0
  81. chipgraph/cli/__init__.py +1688 -0
  82. chipgraph/cli/next_steps.py +137 -0
  83. chipgraph/core/__init__.py +1 -0
  84. chipgraph/core/config/__init__.py +93 -0
  85. chipgraph/core/config/defaults.py +14 -0
  86. chipgraph/core/config/errors.py +32 -0
  87. chipgraph/core/config/loader.py +766 -0
  88. chipgraph/core/config/models.py +845 -0
  89. chipgraph/core/config/templates.py +340 -0
  90. chipgraph/core/contracts/__init__.py +75 -0
  91. chipgraph/core/contracts/_paths.py +26 -0
  92. chipgraph/core/contracts/agent.py +49 -0
  93. chipgraph/core/contracts/artifact.py +68 -0
  94. chipgraph/core/contracts/check.py +76 -0
  95. chipgraph/core/contracts/event.py +70 -0
  96. chipgraph/core/contracts/export.py +128 -0
  97. chipgraph/core/contracts/finding.py +171 -0
  98. chipgraph/core/contracts/gate.py +50 -0
  99. chipgraph/core/contracts/rule.py +177 -0
  100. chipgraph/core/contracts/types.py +45 -0
  101. chipgraph/core/engine/__init__.py +70 -0
  102. chipgraph/core/engine/agent_rule.py +1362 -0
  103. chipgraph/core/engine/baseline.py +283 -0
  104. chipgraph/core/engine/decide.py +462 -0
  105. chipgraph/core/engine/fanout.py +1358 -0
  106. chipgraph/core/engine/gate.py +170 -0
  107. chipgraph/core/engine/graph.py +647 -0
  108. chipgraph/core/engine/records.py +60 -0
  109. chipgraph/core/engine/rules.py +92 -0
  110. chipgraph/core/engine/scheduler.py +637 -0
  111. chipgraph/core/engine/select.py +1239 -0
  112. chipgraph/core/engine/targets.py +342 -0
  113. chipgraph/core/model/__init__.py +98 -0
  114. chipgraph/core/model/entities.py +244 -0
  115. chipgraph/core/model/export.py +16 -0
  116. chipgraph/core/model/ingest.py +693 -0
  117. chipgraph/core/model/json_value.py +4 -0
  118. chipgraph/core/model/keys.py +95 -0
  119. chipgraph/core/model/model.py +153 -0
  120. chipgraph/core/model/provenance.py +27 -0
  121. chipgraph/core/model/query.py +583 -0
  122. chipgraph/core/model/registry.py +80 -0
  123. chipgraph/core/model/relations.py +61 -0
  124. chipgraph/core/model/store.py +279 -0
  125. chipgraph/core/plugin_api/__init__.py +81 -0
  126. chipgraph/core/plugin_api/local.py +167 -0
  127. chipgraph/core/plugin_api/pack.py +159 -0
  128. chipgraph/core/plugin_api/protocols.py +177 -0
  129. chipgraph/core/plugin_api/registry.py +111 -0
  130. chipgraph/core/plugin_api/types.py +94 -0
  131. chipgraph/core/runtime/__init__.py +41 -0
  132. chipgraph/core/runtime/executor.py +79 -0
  133. chipgraph/core/runtime/queue.py +565 -0
  134. chipgraph/core/runtime/roles/__init__.py +91 -0
  135. chipgraph/core/runtime/roles/data/author.md +41 -0
  136. chipgraph/core/runtime/roles/data/critic.md +56 -0
  137. chipgraph/core/runtime/roles/data/planner.md +38 -0
  138. chipgraph/core/runtime/roles/data/researcher.md +37 -0
  139. chipgraph/core/runtime/roles/data/rtl-author.md +64 -0
  140. chipgraph/core/runtime/roles/data/tb-author.md +48 -0
  141. chipgraph/core/runtime/roles/data/triage.md +34 -0
  142. chipgraph/core/runtime/roles/policy.py +299 -0
  143. chipgraph/core/runtime/roles/registry.py +52 -0
  144. chipgraph/core/runtime/roles/review.py +275 -0
  145. chipgraph/core/runtime/roles/schema.py +82 -0
  146. chipgraph/core/runtime/roles/skills.py +134 -0
  147. chipgraph/core/runtime/roles/spec.py +232 -0
  148. chipgraph/core/state/__init__.py +1 -0
  149. chipgraph/core/state/artifacts.py +156 -0
  150. chipgraph/core/state/backend.py +117 -0
  151. chipgraph/core/state/findings.py +151 -0
  152. chipgraph/core/state/handoff.py +411 -0
  153. chipgraph/core/state/idempotency.py +59 -0
  154. chipgraph/core/state/journal.py +238 -0
  155. chipgraph/core/state/layout.py +82 -0
  156. chipgraph/core/state/lock.py +195 -0
  157. chipgraph/core/state/trace.py +225 -0
  158. chipgraph/learn/__init__.py +96 -0
  159. chipgraph/learn/draft.py +187 -0
  160. chipgraph/learn/infer.py +580 -0
  161. chipgraph/learn/models.py +145 -0
  162. chipgraph/learn/try_run.py +228 -0
  163. chipgraph/mcp/__init__.py +12 -0
  164. chipgraph/mcp/model_tools.py +167 -0
  165. chipgraph/mcp/server.py +787 -0
  166. chipgraph/orgs/qnsc/naming-v1.yml +113 -0
  167. chipgraph/orgs/qnsc/profile.yml +5 -0
  168. chipgraph/packs/__init__.py +8 -0
  169. chipgraph/packs/assist/__init__.py +16 -0
  170. chipgraph/packs/assist/ask/__init__.py +58 -0
  171. chipgraph/packs/assist/ask/_db.py +57 -0
  172. chipgraph/packs/assist/ask/_project.py +230 -0
  173. chipgraph/packs/assist/ask/answer.py +266 -0
  174. chipgraph/packs/assist/ask/check.py +182 -0
  175. chipgraph/packs/assist/ask/contract.py +115 -0
  176. chipgraph/packs/assist/ask/documents.py +157 -0
  177. chipgraph/packs/assist/ask/fts.py +80 -0
  178. chipgraph/packs/assist/ask/retrieve.py +331 -0
  179. chipgraph/packs/assist/audit/__init__.py +383 -0
  180. chipgraph/packs/assist/pack.yml +13 -0
  181. chipgraph/packs/assist/triage/__init__.py +64 -0
  182. chipgraph/packs/assist/triage/contract.py +94 -0
  183. chipgraph/packs/assist/triage/parse.py +371 -0
  184. chipgraph/packs/assist/triage/question.py +384 -0
  185. chipgraph/packs/assist/triage/report.py +160 -0
  186. chipgraph/packs/assist/triage/rules.py +447 -0
  187. chipgraph/packs/assist/triage/run.py +224 -0
  188. chipgraph/packs/digital_rtl/__init__.py +19 -0
  189. chipgraph/packs/digital_rtl/layout.py +144 -0
  190. chipgraph/packs/digital_rtl/pack.yml +25 -0
  191. chipgraph/packs/digital_rtl/plan/__init__.py +34 -0
  192. chipgraph/packs/digital_rtl/plan/__main__.py +16 -0
  193. chipgraph/packs/digital_rtl/plan/context.py +82 -0
  194. chipgraph/packs/digital_rtl/plan/expand.py +94 -0
  195. chipgraph/packs/digital_rtl/plan/model.py +150 -0
  196. chipgraph/packs/digital_rtl/plan/resolver.py +400 -0
  197. chipgraph/packs/digital_rtl/plan/show.py +176 -0
  198. chipgraph/packs/digital_rtl/pr/__init__.py +11 -0
  199. chipgraph/packs/digital_rtl/pr/body.py +165 -0
  200. chipgraph/packs/digital_rtl/pr/gate.py +110 -0
  201. chipgraph/packs/digital_rtl/pr/open.py +397 -0
  202. chipgraph/packs/digital_rtl/rtl/__init__.py +9 -0
  203. chipgraph/packs/digital_rtl/rtl/context.py +272 -0
  204. chipgraph/packs/digital_rtl/rules/open_pr.yml +30 -0
  205. chipgraph/packs/digital_rtl/rules/plan.yml +28 -0
  206. chipgraph/packs/digital_rtl/rules/plan_expand.yml +29 -0
  207. chipgraph/packs/digital_rtl/rules/pr_gate.yml +27 -0
  208. chipgraph/packs/digital_rtl/rules/review.yml +31 -0
  209. chipgraph/packs/digital_rtl/rules/rtl_module.yml +53 -0
  210. chipgraph/packs/digital_rtl/skills/lang-sv-rtl.md +58 -0
  211. chipgraph/packs/digital_rtl/skills/org-naming.md +29 -0
  212. chipgraph/packs/digital_rtl/skills/plan-modules.md +63 -0
  213. chipgraph/packs/digital_rtl/skills/review-diff.md +41 -0
  214. chipgraph/packs/digital_rtl/targets/plan.yml +11 -0
  215. chipgraph/packs/digital_rtl/targets/rtl.yml +20 -0
  216. chipgraph/packs/digital_rtl/wrapper/__init__.py +24 -0
  217. chipgraph/packs/digital_rtl/wrapper/common.py +171 -0
  218. chipgraph/packs/digital_rtl/wrapper/context.py +222 -0
  219. chipgraph/packs/digital_rtl/wrapper/emacs.py +626 -0
  220. chipgraph/packs/digital_rtl/wrapper/expansion.py +304 -0
  221. chipgraph/packs/digital_rtl/wrapper/lisp.py +504 -0
  222. chipgraph/packs/digital_rtl/wrapper/resolver.py +60 -0
  223. chipgraph/packs/digital_rtl/wrapper/rules/wrapper.yml +41 -0
  224. chipgraph/packs/digital_rtl/wrapper/rules/wrapper_gen.yml +35 -0
  225. chipgraph/packs/digital_rtl/wrapper/skills/wrapper-auto.md +120 -0
  226. chipgraph/packs/dv/__init__.py +15 -0
  227. chipgraph/packs/dv/declaration.py +392 -0
  228. chipgraph/packs/dv/interface.py +596 -0
  229. chipgraph/packs/dv/pack.yml +14 -0
  230. chipgraph/packs/dv/rules/tb_module.yml +42 -0
  231. chipgraph/packs/dv/skills/dv/cocotb.md +72 -0
  232. chipgraph/packs/spec_core/__init__.py +9 -0
  233. chipgraph/packs/spec_core/extract/__init__.py +12 -0
  234. chipgraph/packs/spec_core/extract/_markdown.py +371 -0
  235. chipgraph/packs/spec_core/extract/_text.py +116 -0
  236. chipgraph/packs/spec_core/extract/mas.py +975 -0
  237. chipgraph/packs/spec_core/gen/__init__.py +11 -0
  238. chipgraph/packs/spec_core/gen/agents_md/__init__.py +148 -0
  239. chipgraph/packs/spec_core/gen/agents_md/_context.py +239 -0
  240. chipgraph/packs/spec_core/gen/agents_md/_markers.py +131 -0
  241. chipgraph/packs/spec_core/gen/agents_md/agents_md.md.j2 +178 -0
  242. chipgraph/packs/spec_core/gen/diagram/__init__.py +110 -0
  243. chipgraph/packs/spec_core/gen/diagram/__main__.py +44 -0
  244. chipgraph/packs/spec_core/gen/diagram/_diagrams.py +403 -0
  245. chipgraph/packs/spec_core/gen/diagram/_scene.py +163 -0
  246. chipgraph/packs/spec_core/pack.yml +6 -0
  247. chipgraph/py.typed +0 -0
  248. chipgraph-0.1.0.dist-info/METADATA +53 -0
  249. chipgraph-0.1.0.dist-info/RECORD +252 -0
  250. chipgraph-0.1.0.dist-info/WHEEL +4 -0
  251. chipgraph-0.1.0.dist-info/entry_points.txt +61 -0
  252. 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
+ ]