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
42 changes: 42 additions & 0 deletions scripts/eth_sensitivity/build_candidate_matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Generate a small Ethernet placement-sensitivity test matrix."""

import json
from pathlib import Path


MATRIX = {
"baseline_seed_2": {
"description": "Known-good forced 10/100 Ethernet baseline seed.",
"quartus_seed": 2,
"env": {},
"expected": "ping_pass",
},
"baseline_seed_5": {
"description": "Previously observed seed-sensitive Ethernet failure candidate.",
"quartus_seed": 5,
"env": {},
"expected": "investigate",
},
"buttons_csr_seed_2": {
"description": "Opt-in KEY1-KEY3 CSR image using the known-good seed.",
"quartus_seed": 2,
"env": {"DE2_ENABLE_BUTTONS_CSR": "1"},
"expected": "investigate",
},
"ps2_rx_seed_2": {
"description": "Opt-in PS/2 receive probe image using the known-good seed.",
"quartus_seed": 2,
"env": {"DE2_ENABLE_PS2_RX_PROBE": "1"},
"expected": "investigate",
},
}


def main() -> None:
out_path = Path(__file__).with_name("candidate_matrix.json")
out_path.write_text(json.dumps(MATRIX, indent=2) + "\n", encoding="utf-8")
print(f"Wrote {out_path}")


if __name__ == "__main__":
main()
30 changes: 30 additions & 0 deletions scripts/eth_sensitivity/candidate_matrix.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"baseline_seed_2": {
"description": "Known-good forced 10/100 Ethernet baseline seed.",
"quartus_seed": 2,
"env": {},
"expected": "ping_pass"
},
"baseline_seed_5": {
"description": "Previously observed seed-sensitive Ethernet failure candidate.",
"quartus_seed": 5,
"env": {},
"expected": "investigate"
},
"buttons_csr_seed_2": {
"description": "Opt-in KEY1-KEY3 CSR image using the known-good seed.",
"quartus_seed": 2,
"env": {
"DE2_ENABLE_BUTTONS_CSR": "1"
},
"expected": "investigate"
},
"ps2_rx_seed_2": {
"description": "Opt-in PS/2 receive probe image using the known-good seed.",
"quartus_seed": 2,
"env": {
"DE2_ENABLE_PS2_RX_PROBE": "1"
},
"expected": "investigate"
}
}
37 changes: 37 additions & 0 deletions scripts/eth_sensitivity/eth_qsf_checklist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Report Ethernet-related QSF assignments for quick pass/fail comparison."""

import argparse
from pathlib import Path


KEYWORDS = ("eth", "enet", "rgmii", "mdio", "mdc")


def ethernet_lines(qsf_path: Path) -> list[str]:
lines: list[str] = []
for raw_line in qsf_path.read_text(encoding="utf-8", errors="ignore").splitlines():
line = raw_line.strip()
if not line or line.startswith("#"):
continue
lower = line.lower()
if any(keyword in lower for keyword in KEYWORDS):
lines.append(line)
return lines


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("qsf", type=Path, help="Path to de2_115_vga_platform.qsf")
args = parser.parse_args()

if not args.qsf.exists():
raise SystemExit(f"QSF not found: {args.qsf}")

lines = ethernet_lines(args.qsf)
print(f"Ethernet-related QSF assignments in {args.qsf}: {len(lines)}")
for line in lines:
print(line)


if __name__ == "__main__":
main()
52 changes: 52 additions & 0 deletions scripts/eth_sensitivity/eth_timing_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Extract Ethernet-related timing lines from a Quartus STA report."""

import argparse
from pathlib import Path


KEYWORDS = ("eth", "enet", "rgmii", "mdio", "mdc")
CONTEXT_MARKERS = (
"setup",
"hold",
"recovery",
"removal",
"unconstrained",
"slack",
"violat",
"clock",
)


def interesting_lines(report_path: Path) -> list[tuple[int, str]]:
matches: list[tuple[int, str]] = []
for lineno, raw_line in enumerate(
report_path.read_text(encoding="utf-8", errors="ignore").splitlines(),
start=1,
):
lower = raw_line.lower()
if any(keyword in lower for keyword in KEYWORDS) and any(
marker in lower for marker in CONTEXT_MARKERS
):
matches.append((lineno, raw_line.rstrip()))
return matches


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("sta_report", type=Path, help="Path to Quartus .sta.rpt")
parser.add_argument("--limit", type=int, default=80)
args = parser.parse_args()

if not args.sta_report.exists():
raise SystemExit(f"STA report not found: {args.sta_report}")

matches = interesting_lines(args.sta_report)
print(f"Ethernet-related timing lines in {args.sta_report}: {len(matches)}")
for lineno, line in matches[: args.limit]:
print(f"{lineno}: {line}")
if len(matches) > args.limit:
print(f"... truncated {len(matches) - args.limit} additional lines")


if __name__ == "__main__":
main()
61 changes: 61 additions & 0 deletions scripts/eth_sensitivity/local_validation_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Ethernet Placement Sensitivity Validation

This is a source-only salvage of the useful idea from PR #6. It does not change
RTL, QSF, workflows, generated outputs, SOFs, VCDs, or board defaults.

## Goal

Identify why some otherwise successful Quartus builds lose Ethernet ping or
Etherbone reachability when the design grows, the seed changes, or opt-in debug
features are enabled.

## Candidate Matrix

Use `candidate_matrix.json` as the initial set of images to compare:

- `baseline_seed_2`: known-good forced 10/100 baseline.
- `baseline_seed_5`: previously observed sensitivity candidate.
- `buttons_csr_seed_2`: opt-in KEY1-KEY3 CSR candidate.
- `ps2_rx_seed_2`: opt-in PS/2 receive-probe candidate.

Regenerate the JSON with:

```powershell
python scripts\eth_sensitivity\build_candidate_matrix.py
```

## Local Bench Sequence

For each candidate:

1. Build the SoC/bitstream with the candidate seed and environment variables.
2. Program the DE2-115.
3. Confirm UART boot.
4. Confirm Ethernet link/in-band status.
5. Run host ping to `192.168.178.50`.
6. Run the existing Etherbone CSR smoke path.
7. Save `.sta.rpt`, `.fit.rpt`, and `.qsf` for pass/fail comparison.

## Report Checks

Use the helper scripts after each build:

```powershell
python scripts\eth_sensitivity\eth_qsf_checklist.py de2_115_vga_platform.qsf
python scripts\eth_sensitivity\eth_timing_parser.py de2_115_vga_platform.sta.rpt
```

Compare passing and failing reports for Ethernet clocks, RGMII/MII paths, MDIO
paths, unconstrained paths, and timing warnings.

## Acceptance

A candidate is useful only if it records:

- Git commit and branch.
- Quartus seed and opt-in environment variables.
- SOF checksum or build identifier.
- UART Ethernet status line.
- Ping result.
- Etherbone CSR result.
- Timing/report artifacts retained outside the source tree.
Loading