Coverage for tsfpga/build_result.py: 65%
43 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-09 00:40 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-09 00:40 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the tsfpga project, a project platform for modern FPGA development.
5# https://tsfpga.com
6# https://github.com/tsfpga/tsfpga
7# --------------------------------------------------------------------------------------------------
9from __future__ import annotations
12class BuildResult:
13 """
14 The result of a build, in a backend-agnostic form.
15 Each build backend has its own subclass, which is what is actually returned by the build
16 methods: :class:`.VivadoBuildResult` and :class:`.YosysBuildResult`.
18 Attributes:
19 name (`str`): The name of the build.
20 success (`bool`): True if the build and all pre- and post hooks succeeded.
21 synthesis_size (`dict`): A dictionary with the utilization of primitives for the
22 synthesized design.
23 Will be ``None`` if synthesis failed or did not run.
24 """
26 def __init__(self, name: str) -> None:
27 """
28 Arguments:
29 name: The name of the build.
30 """
31 self.name = name
32 self.success: bool = True
34 self.synthesis_size: dict[str, int] | None = None
36 def _get_size_to_report(self) -> tuple[str, dict[str, int]] | None:
37 """
38 Return:
39 The name of the build step, and the size that shall be reported for it.
40 ``None`` if no size is set.
41 """
42 if self.synthesis_size:
43 return "synthesis", self.synthesis_size
45 return None
47 def size_summary(self) -> str | None:
48 """
49 Return a string with a formatted message of the size.
51 Return:
52 A human-readable message of the latest size.
53 ``None`` if no size is set.
54 """
55 size_to_report = self._get_size_to_report()
56 if size_to_report is None:
57 return None
59 build_step, size = size_to_report
61 values = [(key, _to_thousands_separated_string(value)) for key, value in size.items()]
62 max_key_length = max(len(key) for key, _ in values)
63 max_value_length = max(len(value) for _, value in values)
65 result = f"Size of {self.name} after {build_step}:"
66 for key, value in values:
67 pad = " " * (max_key_length - len(key) + max_value_length - len(value))
68 result += f"\n - {key}: {pad}{value}"
70 return result
72 def report(self) -> str | None:
73 """
74 Return a report of the build result. Includes all metrics and information that has been
75 extracted from the build tool's reports.
76 """
77 return self.size_summary()
80def _to_engineering_string(value: float) -> str:
81 """
82 Returns float/int value formatted with an SI prefix, for printing with a unit.
83 For example, ``1.5625e8`` becomes ``156.25 M``.
84 """
85 if value == 0:
86 return "0 "
88 sign = ""
89 if value < 0:
90 value = -value
91 sign = "-"
93 exponent = 0
95 while value < 1:
96 value *= 1000
97 exponent -= 1
98 while value >= 1000:
99 value /= 1000
100 exponent += 1
102 prefix = "" if exponent == 0 else "yzafpnum*kMGTPEZY"[exponent + 8]
104 return f"{sign}{value:.2f} {prefix}"
107def _to_thousands_separated_string(value: int) -> str:
108 """
109 Returns an integer formatted with thousand separators, for printing.
110 For example, ``156250000`` becomes ``156 250 000``.
111 """
112 return f"{value:_}".replace("_", " ")