hwcomponents 1.0.81__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hwcomponents/__init__.py +10 -0
- hwcomponents/_logging.py +99 -0
- hwcomponents/_model_wrapper.py +461 -0
- hwcomponents/_util.py +14 -0
- hwcomponents/_version.py +34 -0
- hwcomponents/_version_scheme.py +23 -0
- hwcomponents/find_models.py +250 -0
- hwcomponents/hwcomponents.py +77 -0
- hwcomponents/model.py +547 -0
- hwcomponents/scaling/__init__.py +7 -0
- hwcomponents/scaling/scalefuncs.py +119 -0
- hwcomponents/scaling/techscaling.py +185 -0
- hwcomponents/select_models.py +466 -0
- hwcomponents-1.0.81.dist-info/METADATA +34 -0
- hwcomponents-1.0.81.dist-info/RECORD +19 -0
- hwcomponents-1.0.81.dist-info/WHEEL +5 -0
- hwcomponents-1.0.81.dist-info/entry_points.txt +3 -0
- hwcomponents-1.0.81.dist-info/top_level.txt +1 -0
- hwcomponents-1.0.81.dist-info/zip-safe +1 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# CMOS scaling based on: Aaron Stillmaker, Bevan Baas, Scaling equations for the
|
|
2
|
+
# accurate prediction of CMOS device performance from 180nm to 7nm, Integration,
|
|
3
|
+
# Volume 58, 2017, Pages 74-81, ISSN 0167-9260,
|
|
4
|
+
# https://doi.org/10.1016/j.vlsi.2017.02.002.
|
|
5
|
+
# Scaling from tech node X to tech node Y involves multiplying area from
|
|
6
|
+
# AREA_SCALING[X][Y].
|
|
7
|
+
from math import ceil, floor
|
|
8
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
TECH_NODES = [130e-9, 90e-9, 65e-9, 45e-9, 32e-9, 20e-9, 16e-9, 14e-9, 10e-9, 7e-9]
|
|
12
|
+
AREA_SCALING = [
|
|
13
|
+
[1, 0.44, 0.23, 0.16, 0.072, 0.033, 0.03, 0.027, 0.016, 0.0092],
|
|
14
|
+
[2.3, 1, 0.53, 0.35, 0.16, 0.075, 0.067, 0.061, 0.036, 0.021],
|
|
15
|
+
[4.3, 1.9, 1, 0.66, 0.31, 0.14, 0.13, 0.12, 0.068, 0.039],
|
|
16
|
+
[6.4, 2.8, 1.5, 1, 0.46, 0.21, 0.19, 0.17, 0.1, 0.059],
|
|
17
|
+
[14, 6.1, 3.3, 2.2, 1, 0.46, 0.41, 0.38, 0.22, 0.13],
|
|
18
|
+
[30, 13, 7.1, 4.7, 2.2, 1, 0.89, 0.82, 0.48, 0.28],
|
|
19
|
+
[34, 15, 7.9, 5.3, 2.4, 1.1, 1, 0.91, 0.54, 0.31],
|
|
20
|
+
[37, 16, 8.7, 5.8, 2.7, 1.2, 1.1, 1, 0.59, 0.34],
|
|
21
|
+
[63, 28, 15, 9.8, 4.5, 2.1, 1.9, 1.7, 1, 0.58],
|
|
22
|
+
[110, 48, 25, 17, 7.8, 3.6, 3.2, 2.9, 1.7, 1],
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
# Scaling from tech node X to tech node Y involves multiplying energy by
|
|
26
|
+
# ENERGY_SCALING[Y][0]Vdd^2+ENERGY_SCALING[Y][1]Vdd+ENERGY_SCALING[Y][2] and
|
|
27
|
+
# dividing by
|
|
28
|
+
# ENERGY_SCALING[X][0]Vdd^2+ENERGY_SCALING[X][1]Vdd+ENERGY_SCALING[X][2]
|
|
29
|
+
ENERGY_SCALING = [
|
|
30
|
+
[7.171, -6.709, 2.904],
|
|
31
|
+
[4.762, -4.781, 2.092],
|
|
32
|
+
[3.755, -4.398, 1.975],
|
|
33
|
+
[1.103, -0.362, 0.2767],
|
|
34
|
+
[0.9559, -0.7823, 0.471],
|
|
35
|
+
[0.373, -0.1582, 0.04104],
|
|
36
|
+
[0.2958, -0.1241, 0.03024],
|
|
37
|
+
[0.2363, -0.09675, 0.02239],
|
|
38
|
+
[0.2068, -0.09311, 0.02375],
|
|
39
|
+
[0.1776, -0.09097, 0.02447],
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _get_technology_node_index(tech_node: float) -> float:
|
|
44
|
+
"""Returns the index of the technology node in the TECH_NODES array.
|
|
45
|
+
Interpolates if necessary."""
|
|
46
|
+
larger_idx, smaller_idx = None, None
|
|
47
|
+
for i, t in enumerate(TECH_NODES):
|
|
48
|
+
if tech_node <= t:
|
|
49
|
+
larger_idx = i
|
|
50
|
+
if tech_node >= t:
|
|
51
|
+
smaller_idx = i
|
|
52
|
+
break
|
|
53
|
+
|
|
54
|
+
failed = larger_idx is None or smaller_idx is None
|
|
55
|
+
|
|
56
|
+
assert not failed, (
|
|
57
|
+
f"Technology node {tech_node} not supported. Ensure all technology "
|
|
58
|
+
f"nodes are in the range [{TECH_NODES[-1]}, {TECH_NODES[0]}]"
|
|
59
|
+
)
|
|
60
|
+
l_node, s_node = TECH_NODES[larger_idx], TECH_NODES[smaller_idx]
|
|
61
|
+
if larger_idx == smaller_idx:
|
|
62
|
+
return larger_idx
|
|
63
|
+
interp = (tech_node - s_node) / (l_node - s_node)
|
|
64
|
+
return larger_idx + (smaller_idx - larger_idx) * interp
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _constrain_to_tech_nodes(tech_node: float):
|
|
68
|
+
if tech_node < min(TECH_NODES):
|
|
69
|
+
return min(TECH_NODES), tech_node / min(TECH_NODES)
|
|
70
|
+
if tech_node > max(TECH_NODES):
|
|
71
|
+
return max(TECH_NODES), tech_node / max(TECH_NODES)
|
|
72
|
+
return tech_node, 1
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def tech_node_area(to_node: float, from_node: float) -> float:
|
|
76
|
+
"""
|
|
77
|
+
Returns the scaling factor for area from the technology node
|
|
78
|
+
`from_node` to the technology node `to_node`. Interpolates if necessary.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
to_node: The technology node to scale to.
|
|
82
|
+
from_node: The technology node to scale from.
|
|
83
|
+
Returns:
|
|
84
|
+
The scaling factor for area.
|
|
85
|
+
"""
|
|
86
|
+
from_node, x = _constrain_to_tech_nodes(from_node)
|
|
87
|
+
to_node, y = _constrain_to_tech_nodes(to_node)
|
|
88
|
+
scale = y / x
|
|
89
|
+
|
|
90
|
+
x = _get_technology_node_index(from_node)
|
|
91
|
+
y = _get_technology_node_index(to_node)
|
|
92
|
+
|
|
93
|
+
# Any unaccounted for scaling with "scale" variable is assumed to scale
|
|
94
|
+
# linearly with tech node based on IDRS 2016 and 2017 predicted estimated
|
|
95
|
+
# SoC area
|
|
96
|
+
return scale * sum(
|
|
97
|
+
[
|
|
98
|
+
AREA_SCALING[floor(x)][floor(y)] * (1 - x % 1) * (1 - y % 1),
|
|
99
|
+
AREA_SCALING[floor(x)][ceil(y)] * (1 - x % 1) * (y % 1),
|
|
100
|
+
AREA_SCALING[ceil(x)][floor(y)] * (x % 1) * (1 - y % 1),
|
|
101
|
+
AREA_SCALING[ceil(x)][ceil(y)] * (x % 1) * (y % 1),
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def tech_node_energy(
|
|
107
|
+
to_node: float, from_node: float, vdd: Union[float, None] = None
|
|
108
|
+
) -> float:
|
|
109
|
+
"""Returns the scaling factor for energy from the technology node
|
|
110
|
+
`from_node` to the technology node `to_node`. Interpolates if necessary.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
to_node: The technology node to scale to.
|
|
114
|
+
from_node: The technology node to scale from.
|
|
115
|
+
vdd: The voltage to scale by. If not provided, 0.8V is used.
|
|
116
|
+
Returns:
|
|
117
|
+
The scaling factor for energy.
|
|
118
|
+
"""
|
|
119
|
+
# Based on IRDS 2022, energy stops scaling after 1nm
|
|
120
|
+
from_node = max(from_node, 1e-9)
|
|
121
|
+
to_node = max(to_node, 1e-9)
|
|
122
|
+
|
|
123
|
+
from_node, x = _constrain_to_tech_nodes(from_node)
|
|
124
|
+
to_node, y = _constrain_to_tech_nodes(to_node)
|
|
125
|
+
scale = (y / x) ** 0.5
|
|
126
|
+
|
|
127
|
+
x = _get_technology_node_index(from_node)
|
|
128
|
+
y = _get_technology_node_index(to_node)
|
|
129
|
+
|
|
130
|
+
if vdd is None:
|
|
131
|
+
vdd = 0.8
|
|
132
|
+
# Outer sum does linear interpolation
|
|
133
|
+
x_e_factor = sum(
|
|
134
|
+
[
|
|
135
|
+
# These sums do aVdd^2 + bVdd + c
|
|
136
|
+
sum(ENERGY_SCALING[floor(x)][i] * vdd ** (2 - i) for i in range(3))
|
|
137
|
+
* (1 - x % 1),
|
|
138
|
+
sum(ENERGY_SCALING[ceil(x)][i] * vdd ** (2 - i) for i in range(3))
|
|
139
|
+
* (x % 1),
|
|
140
|
+
]
|
|
141
|
+
)
|
|
142
|
+
# Outer sum does linear interpolation
|
|
143
|
+
y_e_factor = sum(
|
|
144
|
+
[
|
|
145
|
+
# These sums do aVdd^2 + bVdd + c
|
|
146
|
+
sum(ENERGY_SCALING[floor(y)][i] * vdd ** (2 - i) for i in range(3))
|
|
147
|
+
* (1 - y % 1),
|
|
148
|
+
sum(ENERGY_SCALING[ceil(y)][i] * vdd ** (2 - i) for i in range(3))
|
|
149
|
+
* (y % 1),
|
|
150
|
+
]
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Any unaccounted for scaling with "scale" variable is assumed to scale with
|
|
154
|
+
# square root of tech node based on IDRS 2016 and 2017 predicted estimated
|
|
155
|
+
# fJ/switch
|
|
156
|
+
return y_e_factor / x_e_factor * scale
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def tech_node_leak(
|
|
160
|
+
to_node: float, from_node: float, vdd: Union[float, None] = None
|
|
161
|
+
) -> float:
|
|
162
|
+
"""Returns the scaling factor for leakage power from the technology node
|
|
163
|
+
`from_node` to the technology node `to_node`. Interpolates if necessary.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
to_node: The technology node to scale to.
|
|
167
|
+
from_node: The technology node to scale from.
|
|
168
|
+
vdd: The voltage to scale by. If not provided, 0.8V is used.
|
|
169
|
+
Returns:
|
|
170
|
+
The scaling factor for leakage power.
|
|
171
|
+
"""
|
|
172
|
+
return tech_node_energy(to_node, from_node, vdd)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def tech_node_latency(to_node: float, from_node: float) -> float:
|
|
176
|
+
"""Returns the scaling factor for latency from the technology node
|
|
177
|
+
`from_node` to the technology node `to_node`. Interpolates if necessary.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
to_node: The technology node to scale to.
|
|
181
|
+
from_node: The technology node to scale from.
|
|
182
|
+
Returns:
|
|
183
|
+
The scaling factor for latency.
|
|
184
|
+
"""
|
|
185
|
+
return to_node / from_node
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import copy
|
|
3
|
+
from typing import Any, Callable, Dict, List, Tuple
|
|
4
|
+
from hwcomponents.model import ComponentModel
|
|
5
|
+
from hwcomponents._logging import (
|
|
6
|
+
get_logger,
|
|
7
|
+
pop_all_messages,
|
|
8
|
+
log_all_lines,
|
|
9
|
+
clear_logs,
|
|
10
|
+
)
|
|
11
|
+
from hwcomponents._model_wrapper import (
|
|
12
|
+
ComponentModelWrapper,
|
|
13
|
+
ModelQuery,
|
|
14
|
+
Estimation,
|
|
15
|
+
EstimatorError,
|
|
16
|
+
ModelEstimation,
|
|
17
|
+
FloatEstimation,
|
|
18
|
+
)
|
|
19
|
+
from hwcomponents.find_models import installed_models
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _indent_list_text_block(prefix: str, list_to_print: List[str]):
|
|
23
|
+
if not list_to_print:
|
|
24
|
+
return ""
|
|
25
|
+
return "\n| ".join(
|
|
26
|
+
[f"{prefix}"] + [str(l).replace("\n", "\n| ") for l in list_to_print]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _call_model(
|
|
31
|
+
model: ComponentModelWrapper,
|
|
32
|
+
query: ModelQuery,
|
|
33
|
+
target_func: Callable,
|
|
34
|
+
) -> Estimation:
|
|
35
|
+
# Clear the logger
|
|
36
|
+
pop_all_messages(model.logger)
|
|
37
|
+
try:
|
|
38
|
+
estimation = target_func(query)
|
|
39
|
+
except Exception as e:
|
|
40
|
+
estimation = FloatEstimation(0, success=False, model_name=model.get_name())
|
|
41
|
+
model.logger.error(f"{type(e).__name__}: {e}")
|
|
42
|
+
# Add the full traceback
|
|
43
|
+
import traceback
|
|
44
|
+
|
|
45
|
+
estimation.add_messages(traceback.format_exc().split("\n"))
|
|
46
|
+
return estimation
|
|
47
|
+
|
|
48
|
+
# Add message logs
|
|
49
|
+
estimation.add_messages(pop_all_messages(model.logger))
|
|
50
|
+
estimation.model_name = model.get_name()
|
|
51
|
+
|
|
52
|
+
# See if this estimation matches user requested model and min priority
|
|
53
|
+
attrs = query.component_attributes
|
|
54
|
+
prefix = f"Model {estimation.model_name} did not"
|
|
55
|
+
if attrs.get("model", estimation.model_name) != estimation.model_name:
|
|
56
|
+
estimation.fail(f"{prefix} match requested model {attrs['model']}")
|
|
57
|
+
if attrs.get("min_priority", -float("inf")) > model.model_cls.priority:
|
|
58
|
+
estimation.fail(f"{prefix} meet min_priority {attrs['min_priority']}")
|
|
59
|
+
return estimation
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _get_energy_estimation(
|
|
63
|
+
model: ComponentModelWrapper, query: ModelQuery
|
|
64
|
+
) -> FloatEstimation:
|
|
65
|
+
e = _call_model(model, query, model.get_action_energy_latency)
|
|
66
|
+
if e.success:
|
|
67
|
+
e.value = e.value[0]
|
|
68
|
+
return e
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _get_latency_estimation(
|
|
72
|
+
model: ComponentModelWrapper, query: ModelQuery
|
|
73
|
+
) -> FloatEstimation:
|
|
74
|
+
e = _call_model(model, query, model.get_action_energy_latency)
|
|
75
|
+
if e.success:
|
|
76
|
+
e.value = e.value[1]
|
|
77
|
+
return e
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _get_area_estimation(
|
|
81
|
+
model: ComponentModelWrapper, query: ModelQuery
|
|
82
|
+
) -> FloatEstimation:
|
|
83
|
+
e = _call_model(model, query, model.get_area)
|
|
84
|
+
return e
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _get_leak_power_estimation(
|
|
88
|
+
model: ComponentModelWrapper, query: ModelQuery
|
|
89
|
+
) -> FloatEstimation:
|
|
90
|
+
e = _call_model(model, query, model.get_leak_power)
|
|
91
|
+
return e
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _select_model(
|
|
95
|
+
model: ComponentModelWrapper,
|
|
96
|
+
query: ModelQuery,
|
|
97
|
+
) -> ModelEstimation:
|
|
98
|
+
for required_action in query.required_actions:
|
|
99
|
+
if required_action not in model.get_action_names():
|
|
100
|
+
e = ModelEstimation(0, success=False, model_name=model.get_name())
|
|
101
|
+
e.fail(
|
|
102
|
+
f"Model {model.get_name()} does not support action {required_action}"
|
|
103
|
+
)
|
|
104
|
+
return e
|
|
105
|
+
callfunc = lambda x: ModelEstimation(
|
|
106
|
+
model.get_initialized_subclass(x),
|
|
107
|
+
success=True,
|
|
108
|
+
model_name=model.get_name(),
|
|
109
|
+
)
|
|
110
|
+
return _call_model(model, query, callfunc)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _wrap_model(
|
|
114
|
+
model: ComponentModel | ComponentModelWrapper,
|
|
115
|
+
) -> ComponentModelWrapper:
|
|
116
|
+
if isinstance(model, ComponentModelWrapper):
|
|
117
|
+
return model
|
|
118
|
+
return ComponentModelWrapper(model, model.__name__)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _get_best_estimate(
|
|
122
|
+
query: ModelQuery,
|
|
123
|
+
target: str,
|
|
124
|
+
models: List[ComponentModelWrapper] | List[ComponentModel] = None,
|
|
125
|
+
_return_estimation_object: bool = False,
|
|
126
|
+
_relaxed_component_name_selection: bool = False,
|
|
127
|
+
) -> FloatEstimation | ComponentModel:
|
|
128
|
+
if models is None:
|
|
129
|
+
models = installed_models(_return_wrappers=True)
|
|
130
|
+
|
|
131
|
+
models = [_wrap_model(m) for m in models]
|
|
132
|
+
|
|
133
|
+
if target == "energy":
|
|
134
|
+
est_func = _get_energy_estimation
|
|
135
|
+
elif target == "latency":
|
|
136
|
+
est_func = _get_latency_estimation
|
|
137
|
+
elif target == "area":
|
|
138
|
+
est_func = _get_area_estimation
|
|
139
|
+
elif target == "model":
|
|
140
|
+
est_func = _select_model
|
|
141
|
+
elif target == "leak_power":
|
|
142
|
+
est_func = _get_leak_power_estimation
|
|
143
|
+
else:
|
|
144
|
+
raise ValueError(f"Invalid target: {target}")
|
|
145
|
+
|
|
146
|
+
logging.getLogger("").info(f"{target} estimation for {query}")
|
|
147
|
+
|
|
148
|
+
estimations = []
|
|
149
|
+
|
|
150
|
+
def _get_supported_models(relaxed_component_name_selection: bool):
|
|
151
|
+
supported_models = []
|
|
152
|
+
init_errors = []
|
|
153
|
+
for model in models:
|
|
154
|
+
try:
|
|
155
|
+
if not model.is_component_supported(
|
|
156
|
+
query, relaxed_component_name_selection
|
|
157
|
+
):
|
|
158
|
+
continue
|
|
159
|
+
supported_models.append(model)
|
|
160
|
+
except Exception as e:
|
|
161
|
+
init_errors.append((model, e))
|
|
162
|
+
return supported_models, init_errors
|
|
163
|
+
|
|
164
|
+
supported_models, init_errors = _get_supported_models(
|
|
165
|
+
_relaxed_component_name_selection
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if not supported_models:
|
|
169
|
+
if not models:
|
|
170
|
+
raise EstimatorError(
|
|
171
|
+
f"No models found. Please install hwcomponents models."
|
|
172
|
+
)
|
|
173
|
+
supported_classes = set.union(*[set(p.get_component_names()) for p in models])
|
|
174
|
+
|
|
175
|
+
err_str = []
|
|
176
|
+
if not _relaxed_component_name_selection:
|
|
177
|
+
near_supported, _ = _get_supported_models(True)
|
|
178
|
+
if near_supported:
|
|
179
|
+
err_str.append(
|
|
180
|
+
f"Some component models have similar names to the given component "
|
|
181
|
+
f"name. Did you mean any of the following?\n\t"
|
|
182
|
+
)
|
|
183
|
+
for model in near_supported:
|
|
184
|
+
err_str.append(f"\t{model.get_name()}")
|
|
185
|
+
|
|
186
|
+
if init_errors:
|
|
187
|
+
err_str.append(
|
|
188
|
+
f"Component {query.component_name} is supported by models, but the "
|
|
189
|
+
f"following models could could not be initialized."
|
|
190
|
+
)
|
|
191
|
+
for model, err in init_errors:
|
|
192
|
+
err_str.append(f"\t{model.get_name()}")
|
|
193
|
+
err_str.append(f"\t{str(err).replace("\n", "\n\t")}")
|
|
194
|
+
raise EstimatorError("\n".join(err_str))
|
|
195
|
+
|
|
196
|
+
e = (
|
|
197
|
+
f"Component {query.component_name} is not supported by any models. "
|
|
198
|
+
f"Supported components: " + ", ".join(sorted(supported_classes))
|
|
199
|
+
)
|
|
200
|
+
if err_str:
|
|
201
|
+
e += "\n" + "\n".join(err_str)
|
|
202
|
+
raise EstimatorError(e)
|
|
203
|
+
|
|
204
|
+
estimation = None
|
|
205
|
+
for model in supported_models:
|
|
206
|
+
estimation = est_func(model, copy.deepcopy(query))
|
|
207
|
+
logger = get_logger(model.get_name())
|
|
208
|
+
if not estimation.success:
|
|
209
|
+
estimation.add_messages(pop_all_messages(logger))
|
|
210
|
+
estimations.append((model.priority, estimation))
|
|
211
|
+
else:
|
|
212
|
+
log_all_lines(
|
|
213
|
+
f"HWComponents",
|
|
214
|
+
"info",
|
|
215
|
+
f"{estimation.model_name} returned "
|
|
216
|
+
f"{estimation} with priority {model.priority}. "
|
|
217
|
+
+ _indent_list_text_block("Messages:", estimation.messages),
|
|
218
|
+
)
|
|
219
|
+
break
|
|
220
|
+
else:
|
|
221
|
+
estimation = None
|
|
222
|
+
|
|
223
|
+
full_logs = [
|
|
224
|
+
_indent_list_text_block(
|
|
225
|
+
f"{e.model_name} with priority {a} estimating value: ", e.messages
|
|
226
|
+
)
|
|
227
|
+
for a, e in estimations
|
|
228
|
+
]
|
|
229
|
+
fail_reasons = [
|
|
230
|
+
f"{e.model_name} with priority {a} estimating value: " f"{e.lastmessage()}"
|
|
231
|
+
for a, e in estimations
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
if full_logs:
|
|
235
|
+
log_all_lines(
|
|
236
|
+
"HWComponents",
|
|
237
|
+
"debug",
|
|
238
|
+
_indent_list_text_block("Model logs:", full_logs),
|
|
239
|
+
)
|
|
240
|
+
if fail_reasons:
|
|
241
|
+
log_all_lines(
|
|
242
|
+
"HWComponents",
|
|
243
|
+
"debug",
|
|
244
|
+
_indent_list_text_block("Why models did not estimate:", fail_reasons),
|
|
245
|
+
)
|
|
246
|
+
if fail_reasons:
|
|
247
|
+
log_all_lines(
|
|
248
|
+
"HWComponents",
|
|
249
|
+
"info",
|
|
250
|
+
_indent_list_text_block(
|
|
251
|
+
"Models provided accuracy but failed to estimate:",
|
|
252
|
+
fail_reasons,
|
|
253
|
+
),
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
clear_logs()
|
|
257
|
+
|
|
258
|
+
if estimation is not None and estimation.success:
|
|
259
|
+
if _return_estimation_object:
|
|
260
|
+
return estimation
|
|
261
|
+
return estimation.value
|
|
262
|
+
|
|
263
|
+
clear_logs()
|
|
264
|
+
|
|
265
|
+
raise RuntimeError(
|
|
266
|
+
f"Can not find an {target} model for {query}\n"
|
|
267
|
+
f'{_indent_list_text_block("Logs for models that could estimate query:", full_logs)}\n'
|
|
268
|
+
f'{_indent_list_text_block("Why models did not estimate:", fail_reasons)}\n'
|
|
269
|
+
f'\n.\n.\nTo see a list of available component models, run "hwc --list".'
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def get_energy(
|
|
274
|
+
component_name: str,
|
|
275
|
+
component_attributes: Dict[str, Any],
|
|
276
|
+
action_name: str,
|
|
277
|
+
action_arguments: Dict[str, Any],
|
|
278
|
+
models: List[ComponentModelWrapper] = None,
|
|
279
|
+
_return_estimation_object: bool = False,
|
|
280
|
+
_relaxed_component_name_selection: bool = False,
|
|
281
|
+
) -> float | Estimation:
|
|
282
|
+
"""
|
|
283
|
+
Finds the energy using the best-matching model. "Best" is defined as the
|
|
284
|
+
highest-priority model that has all required attributes specified in
|
|
285
|
+
component_attributes and a matching action with all required arguments specified
|
|
286
|
+
in action_arguments.
|
|
287
|
+
|
|
288
|
+
Parameters
|
|
289
|
+
----------
|
|
290
|
+
component_name: The name of the component.
|
|
291
|
+
component_attributes: The attributes of the component.
|
|
292
|
+
action_name: The name of the action.
|
|
293
|
+
action_arguments: The arguments of the action.
|
|
294
|
+
models: The models to use.
|
|
295
|
+
_return_estimation_object: Whether to return the estimation object instead of
|
|
296
|
+
the energy value.
|
|
297
|
+
_relaxed_component_name_selection: Whether to relax the component name
|
|
298
|
+
selection. Relaxed selection ignores underscores in the component name.
|
|
299
|
+
|
|
300
|
+
Returns
|
|
301
|
+
-------
|
|
302
|
+
The energy in Joules.
|
|
303
|
+
"""
|
|
304
|
+
query = ModelQuery(
|
|
305
|
+
component_name.lower(), component_attributes, action_name, action_arguments
|
|
306
|
+
)
|
|
307
|
+
return _get_best_estimate(
|
|
308
|
+
query,
|
|
309
|
+
"energy",
|
|
310
|
+
models,
|
|
311
|
+
_return_estimation_object,
|
|
312
|
+
_relaxed_component_name_selection,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def get_latency(
|
|
317
|
+
component_name: str,
|
|
318
|
+
component_attributes: Dict[str, Any],
|
|
319
|
+
action_name: str,
|
|
320
|
+
action_arguments: Dict[str, Any],
|
|
321
|
+
models: List[ComponentModelWrapper] = None,
|
|
322
|
+
_return_estimation_object: bool = False,
|
|
323
|
+
_relaxed_component_name_selection: bool = False,
|
|
324
|
+
) -> float | Estimation:
|
|
325
|
+
"""
|
|
326
|
+
Finds the latency using the best-matching model. "Best" is defined as the
|
|
327
|
+
highest-priority model that has all required attributes specified in
|
|
328
|
+
component_attributes and a matching action with all required arguments specified
|
|
329
|
+
in action_arguments.
|
|
330
|
+
|
|
331
|
+
Parameters
|
|
332
|
+
----------
|
|
333
|
+
component_name: The name of the component.
|
|
334
|
+
component_attributes: The attributes of the component.
|
|
335
|
+
action_name: The name of the action.
|
|
336
|
+
action_arguments: The arguments of the action.
|
|
337
|
+
models: The models to use.
|
|
338
|
+
_return_estimation_object: Whether to return the estimation object instead of
|
|
339
|
+
the latency value.
|
|
340
|
+
_relaxed_component_name_selection: Whether to relax the component name
|
|
341
|
+
selection. Relaxed selection ignores underscores in the component name.
|
|
342
|
+
|
|
343
|
+
Returns
|
|
344
|
+
-------
|
|
345
|
+
The latency in seconds.
|
|
346
|
+
"""
|
|
347
|
+
query = ModelQuery(
|
|
348
|
+
component_name.lower(), component_attributes, action_name, action_arguments
|
|
349
|
+
)
|
|
350
|
+
return _get_best_estimate(
|
|
351
|
+
query,
|
|
352
|
+
"latency",
|
|
353
|
+
models,
|
|
354
|
+
_return_estimation_object,
|
|
355
|
+
_relaxed_component_name_selection,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def get_area(
|
|
360
|
+
component_name: str,
|
|
361
|
+
component_attributes: Dict[str, Any],
|
|
362
|
+
models: List[ComponentModelWrapper] = None,
|
|
363
|
+
_return_estimation_object: bool = False,
|
|
364
|
+
_relaxed_component_name_selection: bool = False,
|
|
365
|
+
) -> float | Estimation:
|
|
366
|
+
"""
|
|
367
|
+
Finds the area using the best-matching model. "Best" is defined as the
|
|
368
|
+
highest-priority model that has all required attributes specified in
|
|
369
|
+
component_attributes.
|
|
370
|
+
|
|
371
|
+
Parameters
|
|
372
|
+
----------
|
|
373
|
+
component_name: The name of the component.
|
|
374
|
+
component_attributes: The attributes of the component.
|
|
375
|
+
models: The models to use.
|
|
376
|
+
_return_estimation_object: Whether to return the estimation object instead of
|
|
377
|
+
the area value.
|
|
378
|
+
_relaxed_component_name_selection: Whether to relax the component name
|
|
379
|
+
selection. Relaxed selection ignores underscores in the component name.
|
|
380
|
+
|
|
381
|
+
Returns
|
|
382
|
+
-------
|
|
383
|
+
The area in m^2.
|
|
384
|
+
"""
|
|
385
|
+
query = ModelQuery(component_name.lower(), component_attributes, None, None)
|
|
386
|
+
return _get_best_estimate(
|
|
387
|
+
query,
|
|
388
|
+
"area",
|
|
389
|
+
models,
|
|
390
|
+
_return_estimation_object,
|
|
391
|
+
_relaxed_component_name_selection,
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def get_leak_power(
|
|
396
|
+
component_name: str,
|
|
397
|
+
component_attributes: Dict[str, Any],
|
|
398
|
+
models: List[ComponentModelWrapper] = None,
|
|
399
|
+
_return_estimation_object: bool = False,
|
|
400
|
+
_relaxed_component_name_selection: bool = False,
|
|
401
|
+
) -> float | Estimation:
|
|
402
|
+
"""
|
|
403
|
+
Finds the leak power using the best-matching model. "Best" is defined as the
|
|
404
|
+
highest-priority model that has all required attributes specified in
|
|
405
|
+
component_attributes.
|
|
406
|
+
|
|
407
|
+
Parameters
|
|
408
|
+
----------
|
|
409
|
+
component_name: The name of the component.
|
|
410
|
+
component_attributes: The attributes of the component.
|
|
411
|
+
models: The models to use.
|
|
412
|
+
_relaxed_component_name_selection: Whether to relax the component name
|
|
413
|
+
selection. Relaxed selection ignores underscores in the component name.
|
|
414
|
+
|
|
415
|
+
Returns
|
|
416
|
+
-------
|
|
417
|
+
The leak power in Watts.
|
|
418
|
+
"""
|
|
419
|
+
query = ModelQuery(component_name.lower(), component_attributes, None, None)
|
|
420
|
+
return _get_best_estimate(
|
|
421
|
+
query,
|
|
422
|
+
"leak_power",
|
|
423
|
+
models,
|
|
424
|
+
_return_estimation_object,
|
|
425
|
+
_relaxed_component_name_selection,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def get_model(
|
|
430
|
+
component_name: str,
|
|
431
|
+
component_attributes: Dict[str, Any],
|
|
432
|
+
required_actions: List[str] = (),
|
|
433
|
+
models: List[ComponentModelWrapper] = None,
|
|
434
|
+
_return_estimation_object: bool = False,
|
|
435
|
+
_relaxed_component_name_selection: bool = False,
|
|
436
|
+
) -> ComponentModelWrapper:
|
|
437
|
+
"""
|
|
438
|
+
Finds the best model for the given component. "Best" is defined as the
|
|
439
|
+
highest-priority model that has all required attributes specified in
|
|
440
|
+
component_attributes, and has actions for all of required_actions.
|
|
441
|
+
|
|
442
|
+
Parameters
|
|
443
|
+
----------
|
|
444
|
+
component_name: The name of the component.
|
|
445
|
+
component_attributes: The attributes of the component.
|
|
446
|
+
required_actions: The actions that are required for the component.
|
|
447
|
+
models: The models to use.
|
|
448
|
+
_return_estimation_object: Whether to return the estimation object instead of
|
|
449
|
+
the model wrapper.
|
|
450
|
+
_relaxed_component_name_selection: Whether to relax the component name
|
|
451
|
+
selection. Relaxed selection ignores underscores in the component name.
|
|
452
|
+
|
|
453
|
+
Returns
|
|
454
|
+
-------
|
|
455
|
+
The best model wrapper.
|
|
456
|
+
"""
|
|
457
|
+
query = ModelQuery(
|
|
458
|
+
component_name.lower(), component_attributes, None, None, required_actions
|
|
459
|
+
)
|
|
460
|
+
return _get_best_estimate(
|
|
461
|
+
query,
|
|
462
|
+
"model",
|
|
463
|
+
models,
|
|
464
|
+
_return_estimation_object,
|
|
465
|
+
_relaxed_component_name_selection,
|
|
466
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hwcomponents
|
|
3
|
+
Version: 1.0.81
|
|
4
|
+
Summary: Hardware Component Area, Energy, Latency, and Leak Power Models
|
|
5
|
+
Author-email: Tanner Andrulis <andrulis@mit.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# HWComponents
|
|
11
|
+
The HWComponents (Hardware Components) package, part of the
|
|
12
|
+
[CiMLoop](https://github.com/mit-emze/cimloop) project, provides an interface for the
|
|
13
|
+
estimation of area, energy, latency, and leak power of hardware components in hardware
|
|
14
|
+
architectures. Key features in HWComponents include:
|
|
15
|
+
|
|
16
|
+
[Information about the package is available on the hwcomponents website](https://accelergy-project.github.io/hwcomponents/).
|
|
17
|
+
|
|
18
|
+
## Citing HWComponents
|
|
19
|
+
|
|
20
|
+
If you use this package in your work, please cite the CiMLoop project:
|
|
21
|
+
|
|
22
|
+
```bibtex
|
|
23
|
+
@INPROCEEDINGS{cimloop,
|
|
24
|
+
author={Andrulis, Tanner and Emer, Joel S. and Sze, Vivienne},
|
|
25
|
+
booktitle={2024 IEEE International Symposium on Performance Analysis of Systems and Software (ISPASS)},
|
|
26
|
+
title={CiMLoop: A Flexible, Accurate, and Fast Compute-In-Memory Modeling Tool},
|
|
27
|
+
year={2024},
|
|
28
|
+
volume={},
|
|
29
|
+
number={},
|
|
30
|
+
pages={10-23},
|
|
31
|
+
keywords={Performance evaluation;Accuracy;Computational modeling;Computer architecture;Artificial neural networks;In-memory computing;Data models;Compute-In-Memory;Processing-In-Memory;Analog;Deep Neural Networks;Systems;Hardware;Modeling;Open-Source},
|
|
32
|
+
doi={10.1109/ISPASS61541.2024.00012}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
hwcomponents/__init__.py,sha256=1rdj62DTJFkSr74oWZ2Ih3oRLqkSAP_VjcOQqg6vmB4,266
|
|
2
|
+
hwcomponents/_logging.py,sha256=ReXxWW3rBRCTOQnGiq1LoEfd7PRlQ0WaIA95K_WqD9k,2923
|
|
3
|
+
hwcomponents/_model_wrapper.py,sha256=XJCWH8SrvaWEIy9dPsLbC8C5iJNxzl5WWmSjgGauEgU,16410
|
|
4
|
+
hwcomponents/_util.py,sha256=HNYhpipunPhtxSH682HGnhvSSFiUvj4LCSBUX2aSNM8,528
|
|
5
|
+
hwcomponents/_version.py,sha256=lpdkpSPBg6TUZ9qwEnA5xz_PjPJuagx6OfQbHGcX8R0,706
|
|
6
|
+
hwcomponents/_version_scheme.py,sha256=INbPbxvnFHVM3tWJy8k_Ozfxj73nrOL286DB2-lKuHE,605
|
|
7
|
+
hwcomponents/find_models.py,sha256=iVrEWShdMg3ZydwDaLoDMqo8GZcZkMqGOWsIcU_i7mM,8035
|
|
8
|
+
hwcomponents/hwcomponents.py,sha256=9h43D0IE_9LzVQOQboE3U_t6_lUhgtCpxR1vHAZhrg8,2000
|
|
9
|
+
hwcomponents/model.py,sha256=6ZJ7Lk748hv66IgYvMpduvU5COMiaaA4BzlBq3Obfgk,21865
|
|
10
|
+
hwcomponents/select_models.py,sha256=Q3J44kgKq5eMk-35yjAfy3ASzJ307sTM5Fn_UfhDzlA,15302
|
|
11
|
+
hwcomponents/scaling/__init__.py,sha256=wAAgWFe9D-IWWD8xyYS11Yls3Xiqw-m_T632OTooQyE,180
|
|
12
|
+
hwcomponents/scaling/scalefuncs.py,sha256=rcJkbuA8-1lsdb0qQbCw962SCI71hWZsV8z97ch5hcc,3398
|
|
13
|
+
hwcomponents/scaling/techscaling.py,sha256=EJ0lzMhv2H_71uqKTa2Yic4kREdK7u5BUisGxH-0iwk,6658
|
|
14
|
+
hwcomponents-1.0.81.dist-info/METADATA,sha256=cckcPQ3ladMfRRZXios9-gv_D6kbMNOjfQ2kjgwn3RQ,1438
|
|
15
|
+
hwcomponents-1.0.81.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
hwcomponents-1.0.81.dist-info/entry_points.txt,sha256=ezk1BAMA2WFaiNmzICB9YuUE2x6fSgkmvPR28KV4_B4,103
|
|
17
|
+
hwcomponents-1.0.81.dist-info/top_level.txt,sha256=V_QIQX2zOOFe59WghKgnGJ6CFPVQvz16tByOrlnwvrs,13
|
|
18
|
+
hwcomponents-1.0.81.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
19
|
+
hwcomponents-1.0.81.dist-info/RECORD,,
|