Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ jobs:
- name: Test Python golden model
run: python -m unittest discover -s tests -v

- name: Check generated README evidence
run: python tools/render_readme_assets.py --check

- name: Regress single-lane parameter case
run: python tools/run_regression.py --sim iverilog --vec-len 1 --random-cases 40

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ I kept the scope deliberately small: parameterized SystemVerilog, ready/valid
control, a bit-exact Python reference model, deterministic regression, and CI.
This is **not** a trained AI model or a complete neural-network accelerator.

<p align="center">
<img src="assets/rtl-regression.svg" width="100%" alt="Deterministic RTL regression matrix across vector lengths 1, 8, and 17">
</p>

The figure is generated from the checked-in CI matrix. It counts verification
transactions; it is not a throughput, timing, area, power, FPGA, or silicon
benchmark.

## What is implemented

- Synthesizable SystemVerilog with configurable vector length
Expand Down Expand Up @@ -72,6 +80,7 @@ lane. See [Architecture](docs/architecture.md) for timing and width details.
| `model/dot_product_model.py` | Bit-exact reference arithmetic and packing |
| `tb/tb_int8_dot_product.sv` | Self-checking ready/valid testbench |
| `tools/run_regression.py` | Vector generation, compile, and simulation harness |
| `tools/render_readme_assets.py` | Rebuilds and checks the README evidence figure |
| `tests/` | Python unit tests for the arithmetic contract |
| `docs/` | Architecture and verification notes |

Expand Down
70 changes: 70 additions & 0 deletions assets/rtl-regression.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions tools/render_readme_assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""Render deterministic README evidence from the checked-in CI matrix."""

from __future__ import annotations

import argparse
from pathlib import Path

from run_regression import corner_cases


ROOT = Path(__file__).resolve().parents[1]
OUTPUT = ROOT / "assets" / "rtl-regression.svg"
SCENARIOS = ((1, 40), (8, 200), (17, 100))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Derive evidence scenarios from the workflow

This hard-codes the same vec-len/random-cases values that are already defined by the CI run_regression steps I checked in .github/workflows/ci.yml, so --check only proves the SVG matches this duplicate tuple, not the checked-in CI workload described by the README. If someone changes a workflow regression case or adds/removes one, CI will still report the README evidence as up to date while the figure is stale; use a shared source or parse the workflow commands instead.

Useful? React with 👍 / 👎.



def render() -> str:
rows = []
for vec_len, random_cases in SCENARIOS:
corners = len(corner_cases(vec_len))
rows.append((vec_len, random_cases, corners, random_cases + corners))

max_total = max(total for _, _, _, total in rows)
bars = []
for index, (vec_len, random_cases, corners, total) in enumerate(rows):
x = 126 + index * 190
total_height = 300 * total / max_total
corner_height = 300 * corners / max_total
random_height = total_height - corner_height
y_random = 474 - random_height
y_corner = y_random - corner_height
bars.append(
f'''<g aria-label="VEC_LEN {vec_len}: {total} deterministic RTL transactions">
<rect x="{x}" y="{y_random:.2f}" width="110" height="{random_height:.2f}" rx="8" fill="#e39a32"/>
<rect x="{x}" y="{y_corner:.2f}" width="110" height="{corner_height:.2f}" rx="8" fill="#203c5b"/>
<text x="{x + 55}" y="{y_corner - 14:.2f}" text-anchor="middle" class="value">{total}</text>
<text x="{x + 55}" y="512" text-anchor="middle" class="axis">VEC_LEN {vec_len}</text>
<text x="{x + 55}" y="538" text-anchor="middle" class="small">{corners} corner + {random_cases} random</text>
</g>'''
)

return f'''<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="620" viewBox="0 0 1280 620" role="img" aria-labelledby="title desc">
<title id="title">Edge AI RTL Lab deterministic regression matrix</title>
<desc id="desc">Stacked bars show corner and fixed-seed random transactions for vector lengths 1, 8, and 17. A verification panel lists ten Python model tests, Icarus RTL simulation, and a Yosys structural check.</desc>
<style>
.title {{ font: 700 34px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; fill: #13253b; }}
.subtitle {{ font: 16px system-ui, sans-serif; fill: #566170; }}
.axis {{ font: 700 16px system-ui, sans-serif; fill: #203c5b; }}
.small {{ font: 14px system-ui, sans-serif; fill: #69717a; }}
.value {{ font: 800 23px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; fill: #13253b; }}
.panel-title {{ font: 700 19px system-ui, sans-serif; fill: #fff8e9; }}
.panel-label {{ font: 14px system-ui, sans-serif; fill: #b9c7d4; }}
.panel-value {{ font: 800 24px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; fill: #ffc56d; }}
</style>
<rect width="1280" height="620" rx="28" fill="#fbf6ec"/>
<path d="M0 92 H1280" stroke="#e6dac3"/>
<text x="62" y="54" class="title">VERIFICATION / REGRESSION MATRIX</text>
<text x="62" y="81" class="subtitle">Checked-in CI workload — transaction count, not performance</text>

<g stroke="#e7dcc8" stroke-width="1">
<path d="M74 474 H664"/><path d="M74 324 H664"/><path d="M74 174 H664"/>
</g>
<text x="76" y="157" class="small">211 max transactions</text>
{''.join(bars)}

<g transform="translate(714 126)">
<rect width="504" height="414" rx="22" fill="#13253b"/>
<text x="34" y="49" class="panel-title">WHAT THE PIPELINE CHECKS</text>
<path d="M34 70 H470" stroke="#39516a"/>

<text x="34" y="111" class="panel-label">Python golden-model unit tests</text>
<text x="430" y="111" text-anchor="end" class="panel-value">10</text>

<text x="34" y="169" class="panel-label">RTL transactions across 3 parameter cases</text>
<text x="430" y="169" text-anchor="end" class="panel-value">369</text>

<text x="34" y="227" class="panel-label">Pseudo-random seed</text>
<text x="430" y="227" text-anchor="end" class="panel-value">0x5eed2339</text>

<text x="34" y="285" class="panel-label">Simulation</text>
<text x="430" y="285" text-anchor="end" class="panel-value">Icarus</text>

<text x="34" y="343" class="panel-label">Structural synthesis sanity check</text>
<text x="430" y="343" text-anchor="end" class="panel-value">Yosys</text>

<text x="34" y="386" class="panel-label">No timing, area, power, FPGA, or silicon claim</text>
</g>

<g transform="translate(76 573)">
<rect width="18" height="18" rx="4" fill="#203c5b"/><text x="28" y="15" class="small">named corner cases</text>
<rect x="202" width="18" height="18" rx="4" fill="#e39a32"/><text x="230" y="15" class="small">fixed-seed random cases</text>
</g>
</svg>
'''


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check", action="store_true", help="fail if the committed SVG is stale")
args = parser.parse_args()
expected = render()

if args.check:
if not OUTPUT.exists() or OUTPUT.read_text(encoding="utf-8") != expected:
raise SystemExit(f"stale generated asset: {OUTPUT.relative_to(ROOT)}")
print(f"up to date: {OUTPUT.relative_to(ROOT)}")
return

OUTPUT.parent.mkdir(parents=True, exist_ok=True)
OUTPUT.write_text(expected, encoding="utf-8")
print(f"wrote {OUTPUT.relative_to(ROOT)}")


if __name__ == "__main__":
main()
Loading