Coverage for tsfpga/vivado/build_result_checker.py: 97%
72 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-10 20:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-10 20:51 +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# --------------------------------------------------------------------------------------------------
9# Standard libraries
10from abc import ABC, abstractmethod
12# Local folder libraries
13from .build_result import BuildResult
16class Limit(ABC):
17 """
18 Base class for limit checks.
19 Inherit and implement the check in subclass.
20 """
22 def __init__(self, value: int) -> None:
23 """
24 Arguments:
25 value: The result value shall be compared with this number.
26 """
27 self.value = value
29 @abstractmethod
30 def check(self, result_value: int) -> bool:
31 pass
33 @abstractmethod
34 def __str__(self) -> str:
35 pass
38class LessThan(Limit):
40 """
41 Limit to be used with a checker to see that a figure is less than the specified value.
42 """
44 def check(self, result_value: int) -> bool:
45 return result_value < self.value
47 def __str__(self) -> str:
48 return f"< {self.value}"
51class EqualTo(Limit):
53 """
54 Limit to be used with a checker to see that a figure is equal to the specified value.
55 """
57 def check(self, result_value: int) -> bool:
58 return result_value == self.value
60 def __str__(self) -> str:
61 return str(self.value)
64class BuildResultChecker(ABC):
66 """
67 Base class for build result checkers that check a certain build result value against a limit.
69 Overload in subclass and implement the ``check`` method according to the resource you want
70 to check.
71 """
73 def __init__(self, limit: Limit):
74 """
75 Arguments:
76 limit: The limit that the specified resource shall be checked against.
77 """
78 self.limit = limit
80 @abstractmethod
81 def check(self, build_result: BuildResult) -> bool:
82 """
83 Arguments:
84 build_result: Build result that shall be checked. Should come from a successful build.
86 Return:
87 True if check passed, false otherwise.
88 """
90 def _check_result_value(self, name: str, result_value: int) -> bool:
91 if result_value is not None and self.limit.check(result_value=result_value):
92 print(f"Result check passed for {name}: {result_value} ({self.limit})")
93 return True
95 print(f"Result check failed for {name}. Got {result_value}, expected {self.limit}.")
96 return False
99class MaximumLogicLevel(BuildResultChecker):
101 """
102 Check the maximum logic level of a build result against a limit.
103 """
105 name = "Maximum logic level"
107 def check(self, build_result: BuildResult) -> bool:
108 value = build_result.maximum_logic_level
109 if value is None:
110 raise ValueError("Should only call after successful synthesis")
112 return self._check_result_value(name="maximum logic level", result_value=value)
115class SizeChecker(BuildResultChecker):
117 """
118 Check a build result size value against a limit.
120 Overload and set the correct ``name``, according to the names
121 in the vendor utilization report.
123 Note that since this is to be used by netlist builds it checks the synthesized size, not
124 the implemented one, even if available.
125 """
127 name: str
129 def check(self, build_result: BuildResult) -> bool:
130 if build_result.synthesis_size is None:
131 raise ValueError("Should only call after successful synthesis")
133 if self.name not in build_result.synthesis_size:
134 raise ValueError(
135 f'Synthesis result size does not contain the requested resource: "{self.name}"'
136 )
138 return self._check_result_value(
139 name=self.name, result_value=build_result.synthesis_size[self.name]
140 )
143class TotalLuts(SizeChecker):
144 name = "Total LUTs"
147class LogicLuts(SizeChecker):
148 name = "Logic LUTs"
151class LutRams(SizeChecker):
152 name = "LUTRAMs"
155class Srls(SizeChecker):
156 name = "SRLs"
159class Ffs(SizeChecker):
160 name = "FFs"
163class Ramb36(SizeChecker):
164 name = "RAMB36"
167class Ramb18(SizeChecker):
168 name = "RAMB18"
171class Uram(SizeChecker):
172 name = "URAM"
175class DspBlocks(SizeChecker):
177 """
178 In Vivado pre-2020.1 the resource was called "DSP48 Blocks" in the utilization report.
179 After that it is called "DSP Blocks". This class checks for both.
180 """
182 name = "DSP Blocks"
184 def check(self, build_result: BuildResult) -> bool:
185 """
186 Same as super class, but checks for the legacy name as well as the current name.
187 """
188 assert (
189 build_result.synthesis_size is not None
190 ), "Should only call after successful synthesis"
192 legacy_name = "DSP48 Blocks"
194 if legacy_name in build_result.synthesis_size:
195 value = build_result.synthesis_size[legacy_name]
196 else:
197 value = build_result.synthesis_size[self.name]
199 return self._check_result_value(name=self.name, result_value=value)