-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaccelergywrapper.py
182 lines (156 loc) · 6.88 KB
/
accelergywrapper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"""
Formula based on
K. Banerjee and A. Mehrotra, “A power-optimal repeater insertion methodology
for global interconnects in nanometer designs,” IEEE Transactions on Electron
Devices, vol. 49, no. 11, pp. 2001–2007, 2002
Wire capacitance per unit length from
R. Ho, T. Ono, R. D. Hopkins, A. Chow, J. Schauer, F. Y. Liu, and
R. Drost, “High speed and low energy capacitively driven on-chip wires,”
IEEE Journal of Solid-State Circuits, vol. 43, no. 1, pp. 52–60, 2008
Plugin by:
Tanner Andrulis, Jamie Koerner, Brandon Yue
"""
from typing import Dict
from math import sqrt
import re
AREA_ACCURACY = 1
ENERGY_ACCURACY = 90
WIRE_NAMES = ['wire', 'Wire']
WIRE_ACTIONS = ['energy', 'transfer_random']
DEFAULT_SWITCHING_ACTIVITY_FACTOR = 0.15
SWING_VOLTAGE = 0.5
A_CONSTANT = 1.07
WIRE_CAP_PER_UNIT_LENGTH = { #pf/mm to match Timeloop expectations
180: .440,
130: .430,
100: .403,
70: .367,
50: .345,
35: .315,
25: .288,
18: .266,
13: .247,
}
MIN_TECHNODE = min(WIRE_CAP_PER_UNIT_LENGTH.keys())
MAX_TECHNODE = max(WIRE_CAP_PER_UNIT_LENGTH.keys())
def wire_energy_per_unit_length(
tech_node: int, delay_penalty: float, voltage: float, switching_activity: float) -> float:
"""
Returns the energy per unit length of a wire.
tech_node: Technology node of the design in nm
delay_penalty: Maximum delay overhead in a fraction of the optimal delay. 0 for min delay,
1 for doubled delay, 2 for tripled...
voltage: Wire swing voltage in volts
switching_activity: Switching activity factor. The probability that a given transmission will
flip from 0->1 or 1->0.
"""
if isinstance(tech_node, str):
tech_node = int(''.join(re.findall(r'\d', tech_node)))
assert tech_node >= MIN_TECHNODE, \
f'Wire energy can not be calculated for {tech_node}nm. Minimum supported: {MIN_TECHNODE}'
assert tech_node <= MAX_TECHNODE, \
f'Wire energy can not be calculated for {tech_node}nm. Maximum supported: {MAX_TECHNODE}'
assert delay_penalty >= 0, f'Wire energy can not be calculated for delay penalty ' \
f'{delay_penalty}. Acceptable delay penalty must be non-negative'
tech_node_lo = max(x for x in WIRE_CAP_PER_UNIT_LENGTH.keys() if x <= tech_node)
tech_node_hi = min(x for x in WIRE_CAP_PER_UNIT_LENGTH.keys() if x >= tech_node)
c_lo = WIRE_CAP_PER_UNIT_LENGTH[tech_node_lo]
c_hi = WIRE_CAP_PER_UNIT_LENGTH[tech_node_hi]
if tech_node_lo == tech_node_hi:
cap = c_lo
else:
cap = c_lo + (c_hi - c_lo) * (tech_node - tech_node_lo) / (tech_node_hi - tech_node_lo)
delay_penalty += 1 # Add in minimum delay
a = A_CONSTANT
asq = A_CONSTANT**2
dpsq = delay_penalty**2
dp_a_prod1 = asq - 2 * a * dpsq - dpsq + 1
# The product of two design knobs on the wire energy and delay penalty
# This gives values on the mininum energy and delay penalty Pareto curve
x_prod = (asq * dpsq - sqrt((-asq * dpsq + dp_a_prod1) ** 2 - 4 * asq) - (dp_a_prod1)) / (2 * a)
# print(f'Voltage**2: {voltage**2}')
# print(f'Cap: {cap}')
# print(f'1+x_prod: {1+x_prod}')
# print(f'A_CONSTANT: {A_CONSTANT}')
return switching_activity * voltage**2 * cap * (1+ x_prod * A_CONSTANT)
# ==============================================================================
# Wrapper Class
# ==============================================================================
class WireEstimator:
def __init__(self):
self.estimator_name = 'Wire Estimator'
def primitive_action_supported(self, interface: Dict) -> float:
"""
:param interface:
- contains four keys:
1. class_name : string
2. attributes: dictionary of name: value
3. action_name: string
4. arguments: dictionary of name: value
:type interface: dict
:return return the accuracy if supported, return 0 if not
:rtype: int
"""
class_name = interface['class_name']
action_name = interface['action_name']
#print('Asked me for support for {} {}'.format(class_name, action_name))
#print(str(class_name).lower() in WIRE_NAMES and str(action_name).lower() in WIRE_ACTIONS)
if str(class_name).lower() in WIRE_NAMES and str(action_name).lower() in WIRE_ACTIONS:
return ENERGY_ACCURACY
return 0 # if not supported, accuracy is 0
def estimate_energy(self, interface: Dict) -> float:
"""
:param interface:
- contains four keys:
1. class_name : string
2. attributes: dictionary of name: value
3. action_name: string
4. arguments: dictionary of name: value
:return the estimated energy
:rtype float
"""
class_name = interface['class_name']
attributes = interface['attributes']
action_name = interface['action_name']
assert 'technology' in attributes, f'Technology node not specified for wire. Please ' \
f'provide a technology node in nm. Given {attributes}'
assert 'delay_penalty' in attributes, f'Delay penalty not specified for wire. Please ' \
f'provide a maximum acceptable delay penalty as ' \
f'a fraction of the optimal delay. Given {attributes}'
if 'voltage' not in attributes:
print(f'WARNING: Swing voltage not specified for wire. Assuming voltage={SWING_VOLTAGE}V')
if 'switching_activity' not in attributes:
print(f'WARNING: Switching activity not specified for wire. ' \
f'Assuming switching_activity={DEFAULT_SWITCHING_ACTIVITY_FACTOR}')
return wire_energy_per_unit_length(
attributes['technology'],
attributes['delay_penalty'],
attributes.get('voltage', SWING_VOLTAGE),
attributes.get('switching_activity', DEFAULT_SWITCHING_ACTIVITY_FACTOR)
)
def primitive_area_supported(self, interface: Dict) -> float:
"""
:param interface:
- contains two keys:
1. class_name : string
2. attributes: dictionary of name: value
:type interface: dict
:return return the accuracy if supported, return 0 if not
:rtype: int
"""
class_name = interface['class_name']
attributes = interface['attributes']
if str(class_name).lower() in WIRE_NAMES:
return AREA_ACCURACY
return 0 # if not supported, accuracy is 0
def estimate_area(self, interface: Dict) -> float:
"""
:param interface:
- contains two keys:
1. class_name : string
2. attributes: dictionary of name: value
:type interface: dict
:return the estimated area
:rtype: float
"""
return 0 # if not supported, accuracy is 0