Skip to content

Commit b3f19b1

Browse files
committed
Performance tweaks
Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
1 parent 2d48113 commit b3f19b1

9 files changed

Lines changed: 399 additions & 154 deletions

File tree

doc/source/reference/formats/ncdb-format.rst

Lines changed: 281 additions & 53 deletions
Large diffs are not rendered by default.

src/ucis/mem/mem_cover_index_iterator.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,17 @@ class MemCoverIndexIterator(object):
1111

1212
def __init__(self, coveritems : List[MemCoverIndex], mask : CoverTypeT):
1313
self.coveritems = coveritems
14-
self.mask = mask
14+
self.mask = int(mask)
1515
self.idx = 0
16-
16+
1717
def __iter__(self):
1818
return self
19-
19+
2020
def __next__(self):
21-
next = None
22-
23-
while self.idx < len(self.coveritems) and next is None:
21+
mask = self.mask
22+
while self.idx < len(self.coveritems):
2423
n = self.coveritems[self.idx]
25-
26-
if (n.data.type & self.mask) != 0:
27-
next = n
2824
self.idx += 1
29-
30-
if next is None:
31-
raise StopIteration
32-
33-
return next
25+
if (int(n.data.type) & mask) != 0:
26+
return n
27+
raise StopIteration

src/ucis/mem/mem_instance_scope.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,16 @@ def createScope(self,
5050
source : SourceT,
5151
type : ScopeTypeT,
5252
flags : FlagsT) -> 'Scope':
53-
if (type & ScopeTypeT.COVERGROUP) != 0:
53+
itype = int(type)
54+
if (itype & int(ScopeTypeT.COVERGROUP)) != 0:
5455
ret = MemCovergroup(self, name, srcinfo, weight, source)
55-
elif (type & ScopeTypeT.BLOCK) != 0:
56+
elif (itype & int(ScopeTypeT.BLOCK)) != 0:
5657
ret = MemBlockScope(self, name, srcinfo, weight, source, flags)
57-
elif (type & ScopeTypeT.BRANCH) != 0:
58+
elif (itype & int(ScopeTypeT.BRANCH)) != 0:
5859
ret = MemBranchScope(self, name, srcinfo, weight, source, flags)
59-
elif (type & ScopeTypeT.TOGGLE) != 0:
60+
elif (itype & int(ScopeTypeT.TOGGLE)) != 0:
6061
ret = MemToggleScope(self, name, srcinfo, weight, source, flags)
61-
elif (type & ScopeTypeT.FSM) != 0:
62+
elif (itype & int(ScopeTypeT.FSM)) != 0:
6263
from ucis.mem.mem_fsm_scope import MemFSMScope
6364
ret = MemFSMScope(self, name, srcinfo, weight, source, flags)
6465
else:
@@ -96,4 +97,4 @@ def createToggle(self,
9697
def getIthCoverItem(self, i)->CoverItem:
9798
return self.m_cover_item_l[i]
9899

99-
100+

src/ucis/mem/mem_scope_iterator.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,19 @@ class MemScopeIterator(object):
1111
def __init__(self, nodes : List['MemScope'], mask):
1212
self.nodes = nodes
1313
self.idx = 0
14-
self.mask = mask
15-
14+
self.mask = int(mask)
15+
1616
def __iter__(self):
1717
return self
18-
18+
1919
def __next__(self):
2020
next = None
21+
mask = self.mask
2122

22-
while next is None and self.idx < len(self.nodes):
23+
while self.idx < len(self.nodes):
2324
n = self.nodes[self.idx]
24-
# TODO: qualify mask
25-
if (n.getScopeType() & self.mask) != 0:
26-
next = n
27-
2825
self.idx += 1
29-
30-
if next is not None:
31-
break
26+
if (int(n.getScopeType()) & mask) != 0:
27+
return n
3228

33-
if next is None:
34-
raise StopIteration
35-
return next
29+
raise StopIteration

src/ucis/ncdb/dfs_util.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ def dfs_scope_list(db) -> list:
3232
Toggle-pair BRANCH scopes are included (they appear in scope_tree.bin
3333
as TOGGLE_PAIR records but still occupy one DFS slot each).
3434
"""
35+
cached = getattr(db, '_dfs_scope_cache', None)
36+
if cached is not None:
37+
return cached
38+
3539
result = []
3640

3741
def _visit(scope):
@@ -43,4 +47,5 @@ def _visit(scope):
4347
for top_scope in db.scopes(ScopeTypeT.ALL):
4448
_visit(top_scope)
4549

50+
db._dfs_scope_cache = result
4651
return result

src/ucis/ncdb/ncdb_reader.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,26 @@ def _fixup_instance_du_links(db: MemUCIS) -> None:
4444
"""
4545
from ucis.mem.mem_instance_scope import MemInstanceScope
4646

47+
_DU_MASK = 0x000000001F000000
48+
_INSTANCE_VAL = int(ScopeTypeT.INSTANCE)
49+
4750
def _fix_parent(parent):
48-
# Build name → DU map from real (attached) children
51+
children = parent.m_children if hasattr(parent, 'm_children') else list(parent.scopes(ScopeTypeT.ALL))
4952
du_map = {}
50-
for child in parent.scopes(ScopeTypeT.ALL):
51-
if ScopeTypeT.DU_ANY(child.getScopeType()):
53+
instances = []
54+
for child in children:
55+
st = int(child.getScopeType())
56+
if st & _DU_MASK:
5257
du_map[child.getScopeName()] = child
53-
54-
# Replace placeholder DU refs on INSTANCE scopes
55-
for child in parent.scopes(ScopeTypeT.ALL):
56-
if isinstance(child, MemInstanceScope):
57-
du = child.m_du_scope
58-
if du is not None and du.m_parent is None:
59-
# Detached placeholder — replace with real DU if available
60-
real_du = du_map.get(child.getScopeName())
61-
if real_du is not None:
62-
child.m_du_scope = real_du
63-
# Recurse
58+
if st == _INSTANCE_VAL:
59+
instances.append(child)
6460
_fix_parent(child)
61+
for child in instances:
62+
du = child.m_du_scope
63+
if du is not None and du.m_parent is None:
64+
real_du = du_map.get(child.getScopeName())
65+
if real_du is not None:
66+
child.m_du_scope = real_du
6567

6668
_fix_parent(db)
6769

src/ucis/ncdb/scope_tree.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -308,21 +308,26 @@ def _read_regular_scope(self, data: bytes, offset: int, parent, counts_iter):
308308
num_coveritems, offset = decode_varint(data, offset)
309309

310310
child_cover_type = None
311+
at_least = 0
311312
if num_coveritems > 0:
312313
ctv, offset = decode_varint(data, offset)
313314
child_cover_type = CoverTypeT(ctv)
314315
defaults = COVER_TYPE_DEFAULTS.get(child_cover_type, (0, 0, 1))
315316
at_least = at_least_override if at_least_override is not None else defaults[1]
316317

317318
if scope_type == ScopeTypeT.INSTANCE:
318-
# createInstance() requires a DU reference; find the matching DU
319-
# that was already serialized (DU scopes precede INSTANCE in DFS).
320319
du_scope = None
321-
for sibling in parent.scopes(ScopeTypeT.ALL):
322-
if (ScopeTypeT.DU_ANY(sibling.getScopeType())
323-
and sibling.getScopeName() == name):
324-
du_scope = sibling
325-
break
320+
_du_mask = 0x000000001F000000
321+
if hasattr(parent, 'm_children'):
322+
for sibling in parent.m_children:
323+
if (int(sibling.getScopeType()) & _du_mask) and sibling.getScopeName() == name:
324+
du_scope = sibling
325+
break
326+
else:
327+
for sibling in parent.scopes(ScopeTypeT.ALL):
328+
if ScopeTypeT.DU_ANY(sibling.getScopeType()) and sibling.getScopeName() == name:
329+
du_scope = sibling
330+
break
326331
if du_scope is None:
327332
# DU not yet in parent (INSTANCE precedes DU in source ordering).
328333
# Create a detached placeholder so createInstance() can succeed
@@ -350,7 +355,7 @@ def _read_regular_scope(self, data: bytes, offset: int, parent, counts_iter):
350355
cd.data = count
351356
if at_least_override is not None or (child_cover_type and
352357
COVER_TYPE_DEFAULTS.get(child_cover_type, (0,0,1))[1] != 0):
353-
cd.at_least = at_least if 'at_least' in dir() else 0
358+
cd.at_least = at_least
354359
scope.createNextCover(ci_name, cd, None)
355360

356361
# Child scopes

src/ucis/report/coverage_report_builder.py

Lines changed: 59 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -31,32 +31,36 @@ def build(db : 'UCIS') ->'CoverageReport':
3131

3232

3333
def _build(self)->'CoverageReport':
34-
34+
35+
all_coverage = 0.0
36+
all_div = 0
3537
for iscope in self.db.scopes(ScopeTypeT.INSTANCE):
36-
self.build_covergroups(iscope)
37-
38+
cov, div = self.build_covergroups(iscope)
39+
all_coverage += cov
40+
all_div += div
41+
42+
if all_div > 0:
43+
self.report.coverage = all_coverage / all_div
44+
else:
45+
self.report.coverage = 0.0
46+
3847
return self.report
39-
4048

4149
def build_covergroups(self, iscope):
42-
50+
4351
coverage = 0.0
4452
div = 0
45-
53+
4654
for cg_t in iscope.scopes(ScopeTypeT.COVERGROUP):
4755
cg = self.build_covergroup(cg_t)
4856
if cg.weight > 0:
4957
coverage += cg.coverage * cg.weight
5058
div += cg.weight
5159
self.report.covergroups.append(cg)
5260
self.report.covergroup_m[cg.instname] = cg
53-
54-
# Handle case when there are no covergroups
55-
if div > 0:
56-
self.report.coverage = coverage/div
57-
else:
58-
self.report.coverage = 0.0
59-
61+
62+
return coverage, div
63+
6064
def build_covergroup(self, cg_n)->CoverageReport.Covergroup:
6165
cg_r = CoverageReport.Covergroup(
6266
cg_n.getScopeName(),
@@ -85,7 +89,12 @@ def build_covergroup(self, cg_n)->CoverageReport.Covergroup:
8589
for cr in cg_r.crosses:
8690
coverage += cr.coverage * cr.weight
8791
div += cr.weight
88-
92+
93+
for sub in cg_r.covergroups:
94+
if sub.weight > 0:
95+
coverage += sub.coverage * sub.weight
96+
div += sub.weight
97+
8998
if div > 0: coverage /= div
9099

91100
cg_r.coverage = coverage
@@ -96,37 +105,44 @@ def build_coverpoint(self, cp_n : Coverpoint):
96105
cp_r = CoverageReport.Coverpoint(cp_n.getScopeName())
97106
cp_r.weight = cp_n.getWeight()
98107

99-
# Read in bins
108+
# Read in bins — check both direct cover items and typed bin
109+
# sub-scopes (CVGBINSCOPE, IGNOREBINSCOPE, ILLEGALBINSCOPE).
100110
num_hit = 0
101111
total = 0
102-
for ci_n in cp_n.coverItems(CoverTypeT.CVGBIN):
103-
cvg_data = ci_n.getCoverData()
104-
105-
if cvg_data.data >= cvg_data.at_least:
106-
num_hit += 1
107-
108-
cp_r.bins.append(CoverageReport.CoverBin(
109-
ci_n.getName(),
110-
cvg_data.at_least,
111-
cvg_data.data))
112112

113-
total += 1
114-
115-
for ci_n in cp_n.coverItems(CoverTypeT.IGNOREBIN):
116-
cvg_data = ci_n.getCoverData()
117-
118-
cp_r.ignore_bins.append(CoverageReport.CoverBin(
119-
ci_n.getName(),
120-
cvg_data.at_least,
121-
cvg_data.data))
122-
123-
for ci_n in cp_n.coverItems(CoverTypeT.ILLEGALBIN):
124-
cvg_data = ci_n.getCoverData()
125-
126-
cp_r.illegal_bins.append(CoverageReport.CoverBin(
127-
ci_n.getName(),
128-
cvg_data.at_least,
129-
cvg_data.data))
113+
def _collect_items(scope):
114+
"""Yield (cover_item, effective_type) from a scope."""
115+
for ci in scope.coverItems(CoverTypeT.CVGBIN):
116+
yield ci, CoverTypeT.CVGBIN
117+
for ci in scope.coverItems(CoverTypeT.IGNOREBIN):
118+
yield ci, CoverTypeT.IGNOREBIN
119+
for ci in scope.coverItems(CoverTypeT.ILLEGALBIN):
120+
yield ci, CoverTypeT.ILLEGALBIN
121+
122+
# Collect from direct items on the coverpoint
123+
sources = [cp_n]
124+
# Also collect from typed bin sub-scopes
125+
for sub in cp_n.scopes(ScopeTypeT.CVGBINSCOPE):
126+
sources.append(sub)
127+
for sub in cp_n.scopes(ScopeTypeT.IGNOREBINSCOPE):
128+
sources.append(sub)
129+
for sub in cp_n.scopes(ScopeTypeT.ILLEGALBINSCOPE):
130+
sources.append(sub)
131+
132+
for src in sources:
133+
for ci_n, ct in _collect_items(src):
134+
cvg_data = ci_n.getCoverData()
135+
bin_obj = CoverageReport.CoverBin(
136+
ci_n.getName(), cvg_data.at_least, cvg_data.data)
137+
if ct == CoverTypeT.CVGBIN:
138+
cp_r.bins.append(bin_obj)
139+
total += 1
140+
if cvg_data.data >= cvg_data.at_least:
141+
num_hit += 1
142+
elif ct == CoverTypeT.IGNOREBIN:
143+
cp_r.ignore_bins.append(bin_obj)
144+
elif ct == CoverTypeT.ILLEGALBIN:
145+
cp_r.illegal_bins.append(bin_obj)
130146

131147
if total > 0:
132148
cp_r.coverage = (100*num_hit)/total
@@ -161,4 +177,4 @@ def build_cross(self, cr_n : Cross):
161177

162178

163179

164-
180+

src/ucis/scope_type_t.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,6 @@ def DU_ANY(t):
190190
>>> if ScopeTypeT.DU_ANY(scope.getType()):
191191
... print("This is a design unit")
192192
"""
193-
return (t & (ScopeTypeT.DU_MODULE|ScopeTypeT.DU_ARCH|ScopeTypeT.DU_PACKAGE|ScopeTypeT.DU_PROGRAM|ScopeTypeT.DU_INTERFACE)) != 0
193+
return (int(t) & 0x000000001F000000) != 0
194194

195-
195+

0 commit comments

Comments
 (0)