Skip to content

Commit 1ca0d5b

Browse files
authored
Merge pull request #3 from Labeeb2339/codex/project-native-hero
Lead with an authenticated RTL CI transcript
2 parents 6fd552b + cfab0a8 commit 1ca0d5b

5 files changed

Lines changed: 234 additions & 5 deletions

File tree

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ control, a bit-exact Python reference model, deterministic regression, and CI.
1111
This is **not** a trained AI model or a complete neural-network accelerator.
1212

1313
<p align="center">
14-
<img src="assets/rtl-regression.svg" width="100%" alt="Deterministic RTL regression matrix across vector lengths 1, 8, and 17">
14+
<img src="assets/rtl-ci-transcript.svg" width="100%" alt="Successful public CI transcript: Icarus Verilog passed deterministic vectors at lengths 1, 8, and 17, and Yosys found no structural problems">
1515
</p>
1616

17-
The figure is generated from the checked-in CI matrix. It counts verification
18-
transactions; it is not a throughput, timing, area, power, FPGA, or silicon
19-
benchmark.
17+
This transcript comes from successful public
18+
[GitHub Actions run 30224621114](https://github.com/Labeeb2339/edge-ai-rtl-lab/actions/runs/30224621114)
19+
at commit `6fd552b`. The checked-in receipt can be regenerated from the run log
20+
with `python tools/capture_ci_receipt.py`; the SVG renderer consumes that
21+
receipt instead of inventing a result.
2022

2123
## What is implemented
2224

@@ -96,6 +98,14 @@ lane. See [Architecture](docs/architecture.md) for timing and width details.
9698

9799
## Evidence and limits
98100

101+
<p align="center">
102+
<img src="assets/rtl-regression.svg" width="100%" alt="Checked-in RTL regression workload across vector lengths 1, 8, and 17">
103+
</p>
104+
105+
The matrix is generated from the checked-in CI workload. It counts verification
106+
transactions; it is not a throughput, timing, area, power, FPGA, or silicon
107+
benchmark.
108+
99109
The automated evidence in this repository is RTL simulation plus a Yosys
100110
structural synthesis check. No FPGA or ASIC implementation, timing closure,
101111
power measurement, silicon validation, or performance benchmark is claimed.

assets/rtl-ci-transcript.svg

Lines changed: 16 additions & 0 deletions
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"schema_version": "edge-ai-rtl-lab.ci-receipt.v1",
3+
"repository": "Labeeb2339/edge-ai-rtl-lab",
4+
"run_id": 30224621114,
5+
"run_url": "https://github.com/Labeeb2339/edge-ai-rtl-lab/actions/runs/30224621114",
6+
"commit": "6fd552b16f41dfa138c61f06aeb11ac26fb9acd8",
7+
"created_at": "2026-07-26T23:08:54Z",
8+
"conclusion": "success",
9+
"regressions": [
10+
{
11+
"command": "python tools/run_regression.py --sim iverilog --vec-len 1 --random-cases 40",
12+
"summary": "Simulator: iverilog; vectors: 47 (7 corner + 40 deterministic random); seed: 0x5eed2339",
13+
"result": "PASS: 47 vectors checked (VEC_LEN=1, OUT_W=16)"
14+
},
15+
{
16+
"command": "python tools/run_regression.py --sim iverilog --vec-len 8 --random-cases 200",
17+
"summary": "Simulator: iverilog; vectors: 211 (11 corner + 200 deterministic random); seed: 0x5eed2339",
18+
"result": "PASS: 211 vectors checked (VEC_LEN=8, OUT_W=16)"
19+
},
20+
{
21+
"command": "python tools/run_regression.py --sim iverilog --vec-len 17 --random-cases 100",
22+
"summary": "Simulator: iverilog; vectors: 111 (11 corner + 100 deterministic random); seed: 0x5eed2339",
23+
"result": "PASS: 111 vectors checked (VEC_LEN=17, OUT_W=16)"
24+
}
25+
],
26+
"synthesis": {
27+
"command": "yosys -p 'read_verilog -sv -DSYNTHESIS rtl/int8_dot_product.sv; chparam -set VEC_LEN 8 int8_dot_product; hierarchy -check -top int8_dot_product; proc; opt; check -assert; stat'",
28+
"tool": "Yosys 0.33 (git sha1 2584903a060)",
29+
"result": "Found and reported 0 problems.",
30+
"generic_cell_count": 36
31+
}
32+
}

tools/capture_ci_receipt.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env python3
2+
"""Rebuild the checked-in RTL CI receipt from an authenticated GitHub run log."""
3+
4+
from __future__ import annotations
5+
6+
import argparse
7+
import json
8+
import re
9+
import subprocess
10+
from pathlib import Path
11+
12+
ROOT = Path(__file__).resolve().parents[1]
13+
REPOSITORY = "Labeeb2339/edge-ai-rtl-lab"
14+
DEFAULT_RUN_ID = 30224621114
15+
16+
17+
def _gh(*arguments: str) -> str:
18+
completed = subprocess.run(
19+
["gh", *arguments], cwd=ROOT, check=True, capture_output=True, text=True
20+
)
21+
return completed.stdout
22+
23+
24+
def _payload(run_id: int) -> dict[str, object]:
25+
metadata = json.loads(
26+
_gh(
27+
"run",
28+
"view",
29+
str(run_id),
30+
"--repo",
31+
REPOSITORY,
32+
"--json",
33+
"databaseId,headSha,status,conclusion,createdAt,url",
34+
)
35+
)
36+
if metadata.get("status") != "completed" or metadata.get("conclusion") != "success":
37+
raise SystemExit("the selected CI run is not a completed success")
38+
39+
log = _gh("run", "view", str(run_id), "--repo", REPOSITORY, "--log")
40+
commands = re.findall(
41+
r"Run (python tools/run_regression\.py --sim iverilog[^\r\n]+)", log
42+
)
43+
summaries = re.findall(r"(Simulator: iverilog; vectors: [^\r\n]+)", log)
44+
results = re.findall(r"(PASS: \d+ vectors checked \(VEC_LEN=\d+, OUT_W=\d+\))", log)
45+
if not (len(commands) == len(summaries) == len(results) == 3):
46+
raise SystemExit("could not recover the three expected regression records")
47+
48+
yosys_command_match = re.search(r"Run (yosys -p '[^\r\n]+')", log)
49+
yosys_tool_match = re.search(r"(Yosys \d+\.\d+ \(git sha1 [^)]+\))", log)
50+
yosys_result_match = re.search(r"(Found and reported \d+ problems\.)", log)
51+
cell_count_match = re.search(r"Number of cells:\s+(\d+)", log)
52+
if not all(
53+
(yosys_command_match, yosys_tool_match, yosys_result_match, cell_count_match)
54+
):
55+
raise SystemExit("could not recover the Yosys structural-check record")
56+
57+
return {
58+
"schema_version": "edge-ai-rtl-lab.ci-receipt.v1",
59+
"repository": REPOSITORY,
60+
"run_id": metadata["databaseId"],
61+
"run_url": metadata["url"],
62+
"commit": metadata["headSha"],
63+
"created_at": metadata["createdAt"],
64+
"conclusion": metadata["conclusion"],
65+
"regressions": [
66+
{"command": command, "summary": summary, "result": result}
67+
for command, summary, result in zip(
68+
commands, summaries, results, strict=True
69+
)
70+
],
71+
"synthesis": {
72+
"command": yosys_command_match.group(1),
73+
"tool": yosys_tool_match.group(1),
74+
"result": yosys_result_match.group(1),
75+
"generic_cell_count": int(cell_count_match.group(1)),
76+
},
77+
}
78+
79+
80+
def main() -> None:
81+
parser = argparse.ArgumentParser(description=__doc__)
82+
parser.add_argument("--run-id", type=int, default=DEFAULT_RUN_ID)
83+
parser.add_argument("--output", type=Path)
84+
args = parser.parse_args()
85+
output = args.output or ROOT / "evidence" / f"rtl-ci-run-{args.run_id}.json"
86+
if not output.is_absolute():
87+
output = ROOT / output
88+
output.parent.mkdir(parents=True, exist_ok=True)
89+
output.write_text(
90+
json.dumps(_payload(args.run_id), indent=2, sort_keys=False) + "\n",
91+
encoding="utf-8",
92+
newline="\n",
93+
)
94+
print(f"wrote {output.relative_to(ROOT)}")
95+
96+
97+
if __name__ == "__main__":
98+
main()

tools/render_readme_assets.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44
from __future__ import annotations
55

66
import argparse
7+
import json
8+
from html import escape
79
from pathlib import Path
810

911
from run_regression import corner_cases, dot_product_int8
1012

11-
1213
ROOT = Path(__file__).resolve().parents[1]
1314
OUTPUT = ROOT / "assets" / "rtl-regression.svg"
1415
SATURATION_OUTPUT = ROOT / "assets" / "rtl-saturation-evidence.svg"
16+
CI_RECEIPT = ROOT / "evidence" / "rtl-ci-run-30224621114.json"
17+
CI_OUTPUT = ROOT / "assets" / "rtl-ci-transcript.svg"
1518
SCENARIOS = ((1, 40), (8, 200), (17, 100))
1619

1720

@@ -220,13 +223,83 @@ def map_y(value: int) -> float:
220223
'''
221224

222225

226+
def render_ci_transcript() -> str:
227+
receipt = json.loads(CI_RECEIPT.read_text(encoding="utf-8"))
228+
if receipt.get("schema_version") != "edge-ai-rtl-lab.ci-receipt.v1":
229+
raise ValueError("unsupported RTL CI receipt schema")
230+
if receipt.get("conclusion") != "success":
231+
raise ValueError("RTL CI receipt is not a successful run")
232+
regressions = receipt.get("regressions")
233+
synthesis = receipt.get("synthesis")
234+
if not isinstance(regressions, list) or len(regressions) != 3 or not isinstance(synthesis, dict):
235+
raise ValueError("RTL CI receipt is missing expected simulator or synthesis records")
236+
237+
expected_commands = [
238+
f"python tools/run_regression.py --sim iverilog --vec-len {vec_len} --random-cases {random_cases}"
239+
for vec_len, random_cases in SCENARIOS
240+
]
241+
transcript: list[tuple[str, str]] = []
242+
for row, expected_command in zip(regressions, expected_commands, strict=True):
243+
if not isinstance(row, dict) or row.get("command") != expected_command:
244+
raise ValueError("RTL CI regression command does not match the checked-in matrix")
245+
summary = str(row.get("summary", ""))
246+
result = str(row.get("result", ""))
247+
if not summary.startswith("Simulator: iverilog; vectors:") or not result.startswith("PASS:"):
248+
raise ValueError("RTL CI regression record is malformed")
249+
transcript.extend((("command", f"$ {expected_command}"), ("output", summary), ("pass", result)))
250+
251+
if synthesis.get("result") != "Found and reported 0 problems.":
252+
raise ValueError("RTL CI synthesis receipt did not pass its structural check")
253+
transcript.extend(
254+
(
255+
("command", "$ yosys ... hierarchy -check; proc; opt; check -assert; stat"),
256+
("output", str(synthesis.get("tool"))),
257+
("pass", str(synthesis.get("result"))),
258+
(
259+
"muted",
260+
f"generic RTL cells: {int(synthesis.get('generic_cell_count', -1))} (structural count, not area)",
261+
),
262+
)
263+
)
264+
265+
text_lines = []
266+
y = 115
267+
colours = {"command": "#d8dee9", "output": "#9fb1c3", "pass": "#7ee2a8", "muted": "#77889a"}
268+
for kind, value in transcript:
269+
text_lines.append(
270+
f'<text x="54" y="{y}" fill="{colours[kind]}" class="terminal">{escape(value)}</text>'
271+
)
272+
y += 36 if kind == "pass" else 29
273+
274+
run_id = int(receipt["run_id"])
275+
commit = escape(str(receipt["commit"])[:7])
276+
return f'''<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="650" viewBox="0 0 1280 650" role="img" aria-labelledby="ci-title ci-desc">
277+
<title id="ci-title">Successful Edge AI RTL Lab simulator and synthesis transcript</title>
278+
<desc id="ci-desc">A terminal-style rendering of GitHub Actions run {run_id}: Icarus Verilog passed all deterministic vectors at vector lengths 1, 8, and 17, and Yosys reported no structural problems.</desc>
279+
<style>
280+
.chrome {{ font: 600 15px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }}
281+
.terminal {{ font: 15px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }}
282+
</style>
283+
<rect width="1280" height="650" rx="12" fill="#10151c"/>
284+
<rect width="1280" height="58" rx="12" fill="#202833"/>
285+
<path d="M0 46H1280V58H0Z" fill="#202833"/>
286+
<circle cx="25" cy="29" r="7" fill="#ff6b63"/><circle cx="49" cy="29" r="7" fill="#f4bd4f"/><circle cx="73" cy="29" r="7" fill="#62c86f"/>
287+
<text x="104" y="35" fill="#aebdcc" class="chrome">github/actions · rtl-ci · run {run_id} · {commit}</text>
288+
{''.join(text_lines)}
289+
<path d="M32 602H1248" stroke="#2a3440"/>
290+
<text x="54" y="630" fill="#77889a" class="chrome">captured from the successful public CI log · functional simulation + structural synthesis only</text>
291+
</svg>
292+
'''
293+
294+
223295
def main() -> None:
224296
parser = argparse.ArgumentParser(description=__doc__)
225297
parser.add_argument("--check", action="store_true", help="fail if the committed SVG is stale")
226298
args = parser.parse_args()
227299
expected_outputs = {
228300
OUTPUT: render(),
229301
SATURATION_OUTPUT: render_saturation(),
302+
CI_OUTPUT: render_ci_transcript(),
230303
}
231304

232305
if args.check:

0 commit comments

Comments
 (0)