Coverage for apio/scons/report_xilinx.py: 0%
32 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 01:55 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 01:55 +0000
1"""
2This script is called by the `nextpnr-xilinx` tool for generating
3a report, because it lacks the option `--report`
4"""
6import json
7import os
8from pathlib import Path
11# Get the output file path from env. Apio set it up before invoking
12# nextpnr-xilinx which invokes this script.
13value = os.environ.get("APIO_XILINX_REPORT_FILE")
14if not value:
15 raise RuntimeError(
16 "Environment variable APIO_XILINX_REPORT_FILE is not set. "
17 "It is a workaround that Apio uses to pass to the nextpnr-xilinx "
18 "--post-route script the path of the report output file "
19 "'_build/(env)/hardware.pnr'."
20 )
22report_file = Path(value)
24print(f"[report_xilinx.py: writing report to {report_file}]")
27# -- Ignore pylint errores
28# pylint: disable=self-assigning-variable
29# pylint: disable=undefined-variable
30# pylint: disable=invalid-name
32# -- Ignore all hte pylance and Flake8 errors related to
33# -- ctx (that is generated dynamically when calling nextpnr-xilinx)
34ctx = ctx # noqa: F821 # pyright: ignore[reportUndefinedVariable]
36# -- Collect total FPGA resources
37resources_total = {}
39for bel in ctx.getBels():
40 bel_type = ctx.getBelType(bel)
41 resources_total[bel_type] = resources_total.get(bel_type, 0) + 1
43# -- Actual resources used by the current design
44resources_used = {}
45# pyright: ignore[reportUndefinedVariable]
46for cell_name, cell_info in ctx.cells:
47 cell_type = str(cell_info.type)
48 resources_used[cell_type] = resources_used.get(cell_type, 0) + 1
50# -- Build the final blank report json file
51report = {"critical_paths": [], "fmax": {}, "utilization": {}}
53# --- Fill in the report json
54for res, avail in resources_total.items():
55 res_values = {"available": avail, "used": resources_used.get(res, 0)}
56 report["utilization"][res] = res_values
57 # print(f"* {res}: {amount} / {resources_total[res]}")
60# -- Fill in the per-clock fmax. Older nextpnr-xilinx builds (openxc7
61# -- package < 2026-07-17) don't expose the timing results; leave the
62# -- table empty for them, as before.
63if hasattr(ctx, "reportClockFmaxJson"):
64 fmax = json.loads(ctx.reportClockFmaxJson())
65 for clk_net, vals in fmax.items():
66 # -- Yosys internal net names don't survive the report formatter's
67 # -- name cleanup; alias them. '$iopadmap$<port>' is the net yosys
68 # -- inserts for a clock input port -> show the port name itself.
69 if clk_net.startswith("$iopadmap$"):
70 name = clk_net.removeprefix("$iopadmap$")
71 elif clk_net.startswith("$"):
72 name = "(internal) " + clk_net.split("$")[-1]
73 else:
74 name = clk_net
75 report["fmax"][name] = vals
77# -- Generate the report file
78with open(report_file, "w", encoding="utf8") as f:
79 json.dump(report, f, indent=4)