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

1""" 

2This script is called by the `nextpnr-xilinx` tool for generating 

3a report, because it lacks the option `--report` 

4""" 

5 

6import json 

7import os 

8from pathlib import Path 

9 

10 

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 ) 

21 

22report_file = Path(value) 

23 

24print(f"[report_xilinx.py: writing report to {report_file}]") 

25 

26 

27# -- Ignore pylint errores 

28# pylint: disable=self-assigning-variable 

29# pylint: disable=undefined-variable 

30# pylint: disable=invalid-name 

31 

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] 

35 

36# -- Collect total FPGA resources 

37resources_total = {} 

38 

39for bel in ctx.getBels(): 

40 bel_type = ctx.getBelType(bel) 

41 resources_total[bel_type] = resources_total.get(bel_type, 0) + 1 

42 

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 

49 

50# -- Build the final blank report json file 

51report = {"critical_paths": [], "fmax": {}, "utilization": {}} 

52 

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]}") 

58 

59 

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 

76 

77# -- Generate the report file 

78with open(report_file, "w", encoding="utf8") as f: 

79 json.dump(report, f, indent=4)