Skip to content

Commit 219a17e

Browse files
committed
Remove TimingPathEntry since it doesn't map well to innovus, don't use deepcopy because _support_map is flat and it was causing recursion issues, and change how PortSpec works
1 parent 19e74c5 commit 219a17e

1 file changed

Lines changed: 57 additions & 72 deletions

File tree

src/hammer-vlsi/hammer_vlsi/hammer_metrics.py

Lines changed: 57 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -35,46 +35,43 @@ def append(self, child: str) -> 'ModuleSpec':
3535
def is_top(self) -> bool:
3636
return len(self.path) == 0
3737

38+
@property
39+
def to_str(self) -> str:
40+
return "/".join(self.path)
41+
3842
class PortSpec(NamedTuple('PortSpec', [
39-
('path', List[str])
43+
('module', ModuleSpec),
44+
('port', str)
4045
])):
4146
__slots__ = ()
4247

4348
@staticmethod
4449
def from_str(s: str) -> 'PortSpec':
45-
return PortSpec(list(filter(lambda x: x != '', s.split("/"))))
50+
tmp = s.split(':')
51+
if len(tmp) != 2:
52+
raise ValueError("Invalid port spec: " + s)
53+
mod = ModuleSpec.from_str(tmp[0])
54+
return PortSpec(mod, tmp[1])
55+
56+
@property
57+
def to_str(self) -> str:
58+
return self.module.to_str + ":" + self.port
4659

4760
# TODO document me
4861
IRType = Dict[str, Union[str, List[str]]]
4962

50-
# I would like to call these "to" and "from" but "from" is a keyword in python
51-
class TimingPathSpec(NamedTuple('TimingPathSpec', [
52-
('start', Optional[PortSpec]),
53-
('end', Optional[PortSpec]),
54-
('through', Optional[PortSpec])
55-
])):
56-
__slots__ = ()
63+
class MetricsDBEntry:
5764

58-
@staticmethod
59-
def from_ir(ir: IRType) -> 'TimingPathSpec':
60-
start = ir["start"] if "start" in ir else ""
61-
end = ir["end"] if "end" in ir else ""
62-
through = ir["through"] if "through" in ir else ""
63-
assert isinstance(start, str)
64-
assert isinstance(end, str)
65-
assert isinstance(through, str)
66-
startspec = PortSpec.from_str(start) if "start" in ir else None
67-
endspec = PortSpec.from_str(end) if "end" in ir else None
68-
throughspec = PortSpec.from_str(through) if "through" in ir else None
69-
assert startspec is not None or endspec is not None or throughspec is not None, "At least one of start, end, or through must not be None"
70-
return TimingPathSpec(startspec, endspec, throughspec)
65+
@abstractmethod
66+
def register(self, db: 'MetricsDB') -> None:
67+
pass
7168

7269
class CriticalPathEntry(NamedTuple('CriticalPathEntry', [
7370
('module', ModuleSpec),
7471
('clock', Optional[PortSpec]), # TODO make this connect to HammerIR clock entry somehow (HammerClockSpec??)
7572
('target', Optional[float]),
7673
('value', Optional[float])
77-
])):
74+
]), MetricsDBEntry):
7875
__slots__ = ()
7976

8077
@staticmethod
@@ -92,31 +89,13 @@ def from_ir(ir: IRType) -> 'CriticalPathEntry':
9289
except:
9390
raise ValueError("Invalid IR for CriticalPathEntry: {}".format(ir))
9491

95-
class TimingPathEntry(NamedTuple('TimingPathEntry', [
96-
('timing_path', TimingPathSpec),
97-
('clock', Optional[PortSpec]), # TODO same as above
98-
('target', Optional[float]),
99-
('value', Optional[float])
100-
])):
101-
__slots__ = ()
102-
103-
@staticmethod
104-
def from_ir(ir: IRType) -> 'TimingPathEntry':
105-
try:
106-
clock = ir["clock"] if "clock" in ir else ""
107-
assert isinstance(clock, str)
108-
return TimingPathEntry(
109-
TimingPathSpec.from_ir(ir),
110-
PortSpec.from_str(clock) if "clock" in ir else None,
111-
None,
112-
None)
113-
except:
114-
raise ValueError("Invalid IR for TimingPathEntry: {}".format(ir))
92+
def register(self, db: 'MetricsDB') -> None:
93+
db.module_tree.add_module(self.module)
11594

11695
class ModuleAreaEntry(NamedTuple('ModuleAreaEntry', [
11796
('module', ModuleSpec),
11897
('value', Optional[float])
119-
])):
98+
]), MetricsDBEntry):
12099
__slots__ = ()
121100

122101
@staticmethod
@@ -128,16 +107,18 @@ def from_ir(ir: IRType) -> 'ModuleAreaEntry':
128107
ModuleSpec.from_str(mod),
129108
None)
130109
except:
131-
raise ValueError("Invalid IR for TimingPathEntry: {}".format(ir))
110+
raise ValueError("Invalid IR for ModuleAreaEntry: {}".format(ir))
111+
112+
def register(self, db: 'MetricsDB') -> None:
113+
db.module_tree.add_module(self.module)
132114

133115
# TODO document this
134-
MetricsDBEntry = Union[CriticalPathEntry, TimingPathEntry, ModuleAreaEntry]
116+
#MetricsDBEntry = Union[CriticalPathEntry, ModuleAreaEntry]
135117
#SupportMap = Dict[str, Callable[[str, MetricsDBEntry], List[str]]]
136118
SupportMap = Dict[str, Callable[[str, Any], List[str]]]
137119

138120
FromIRMap = {
139121
"critical path": CriticalPathEntry.from_ir,
140-
"timing path": TimingPathEntry.from_ir,
141122
"area": ModuleAreaEntry.from_ir
142123
} # type: Dict[str, Callable[[IRType], MetricsDBEntry]]
143124

@@ -147,7 +128,7 @@ class ModuleTree:
147128

148129
def __init__(self):
149130
self._children = {} # type: Dict[str, ModuleTree]
150-
self._rename_id = index
131+
self._rename_id = ModuleTree.index
151132
ModuleTree.index += 1
152133
self._no_ungroup = False
153134
# More properties go here
@@ -192,24 +173,32 @@ def is_leaf(self) -> bool:
192173
class MetricsDB:
193174

194175
def __init__(self):
195-
self._db = {} # type: Dict[str, MetricsDBEntry]
176+
self._db = {} # type: Dict[str, Dict[str, MetricsDBEntry]]
196177
self._tree = ModuleTree()
197178

198-
def create_entry(self, key: str, entry: MetricsDBEntry) -> None:
199-
if key in self._db:
179+
def create_entry(self, namespace: str, key: str, entry: MetricsDBEntry) -> None:
180+
if namespace not in self._db:
181+
self._db[namespace] = {} # type = Dict[str, MetricsDBEntry]
182+
if key in self._db[namespace]:
200183
raise ValueError("Duplicate entry in MetricsDB: {}".format(key))
201184
else:
202-
self._db[key] = entry
185+
self._db[namespace][key] = entry
186+
203187

204-
def get_entry(self, key: str) -> MetricsDBEntry:
205-
if key in self._db:
206-
return self._db[key]
188+
def get_entry(self, namespace: str, key: str) -> MetricsDBEntry:
189+
if namespace in self._db:
190+
if key in self._db[namespace]:
191+
return self._db[namespace][key]
192+
else:
193+
raise ValueError("Entry not found in MetricsDB: {}".format(key))
207194
else:
208-
raise ValueError("Entry not found in MetricsDB: {}".format(key))
195+
raise ValueError("Namespace not found in MetricsDB: {}".format(namespace))
209196

210-
@property
211-
def entries(self) -> Dict[str, MetricsDBEntry]:
212-
return self._db
197+
def entries(self, namespace: str) -> Dict[str, MetricsDBEntry]:
198+
if namespace in self._db:
199+
return self._db[namespace]
200+
else:
201+
raise ValueError("Namespace not found in MetricsDB: {}".format(namespace))
213202

214203
@property
215204
def module_tree(self) -> ModuleTree:
@@ -232,25 +221,25 @@ def create_metrics_db_from_ir(self, ir: Union[str, TextIO]) -> MetricsDB:
232221
assert(isinstance(y, dict))
233222
# create a db
234223
db = MetricsDB()
235-
if self.namespace in y:
236-
testcases = y[self.namespace]
224+
for namespace in y:
225+
testcases = y[namespace]
237226
for testcase in testcases:
238-
key = "{}.{}".format(self.namespace, testcase)
227+
key = "{}.{}".format(namespace, testcase)
239228
testcase_data = testcases[testcase]
240229
if "type" not in testcase_data:
241230
raise ValueError("Missing \"type\" field in testcase {}".format(testcase))
242231
mtype = testcase_data["type"] # type: str
243232
if mtype in FromIRMap:
244233
entry = FromIRMap[mtype](testcase_data) # type: MetricsDBEntry
245-
db.create_entry(key, entry)
234+
db.create_entry(namespace, key, entry)
246235
else:
247236
raise ValueError("Metric IR field <{}> is not supported. Did you forget to update FromIRMap?".format(mtype))
248237
return db
249238

250239
def generate_metric_requests_from_db(self, db: MetricsDB) -> List[str]:
251240
output = [] # type: List[str]
252-
for key in db.entries:
253-
entry = db.get_entry(key)
241+
for key in db.entries(self.namespace):
242+
entry = db.get_entry(self.namespace, key)
254243
if self._is_supported(entry):
255244
output.extend(self._support_map[entry.__class__.__name__](key, entry))
256245
return output
@@ -274,7 +263,7 @@ class HasAreaMetricSupport(HasMetricSupport):
274263

275264
@property
276265
def _support_map(self) -> SupportMap:
277-
x = copy.deepcopy(super()._support_map) # type: SupportMap
266+
x = copy.copy(super()._support_map) # type: SupportMap
278267
x.update({
279268
'ModuleAreaEntry': self.get_module_area
280269
})
@@ -288,17 +277,13 @@ class HasTimingPathMetricSupport(HasMetricSupport):
288277

289278
@property
290279
def _support_map(self) -> SupportMap:
291-
x = copy.deepcopy(super()._support_map) # type: SupportMap
280+
x = copy.copy(super()._support_map) # type: SupportMap
292281
x.update({
293-
'CriticalPathEntry': self.get_critical_path,
294-
'TimingPathEntry': self.get_timing_path
282+
'CriticalPathEntry': self.get_critical_path
295283
})
296284
return x
297285

298286
@abstractmethod
299287
def get_critical_path(self, key: str, entry: CriticalPathEntry) -> List[str]:
300288
pass
301289

302-
@abstractmethod
303-
def get_timing_path(self, key: str, entry: TimingPathEntry) -> List[str]:
304-
pass

0 commit comments

Comments
 (0)