Skip to content

Commit c3226cd

Browse files
authored
Merge pull request #3989 from AcKoucher/util-segment-correlation
add mode for RC correlation based on segments
2 parents 21ae07f + 9750e41 commit c3226cd

File tree

4 files changed

+156
-2
lines changed

4 files changed

+156
-2
lines changed

flow/util/correlateRC.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ def makeDict():
9494
stack = []
9595
stack_line = None
9696

97+
# For segment mode.
98+
layer_segments = defaultdict(
99+
lambda: {"lengths": [], "resistances": [], "capacitances": []}
100+
)
101+
routing_layers = []
102+
routing_layers_line = None
103+
97104
# indices of relevant layers (routable layers or via layers)
98105
active_layers = set()
99106

@@ -104,6 +111,17 @@ def makeDict():
104111
with open(rc_file) as f:
105112
nonGrtNets = 0
106113
for line in f:
114+
if line.startswith("# routing layers: "):
115+
if routing_layers_line is not None and routing_layers_line != line:
116+
print(f"layer stack inconsistent", file=stderr)
117+
exit(1)
118+
elif routing_layers_line is None:
119+
routing_layers = (
120+
line.removeprefix("# routing layers: ").strip().split(" ")
121+
)
122+
routing_layers_line = line
123+
continue
124+
107125
if line.startswith("# stack: "):
108126
if stack_line is not None and stack_line != line:
109127
print(f"layer stack inconsistent", file=stderr)
@@ -129,7 +147,9 @@ def makeDict():
129147
tokens = line.strip().split(",")
130148

131149
if args.mode == "segment":
132-
pass
150+
layer_segments[tokens[2]]["lengths"].append(float(tokens[3]))
151+
layer_segments[tokens[2]]["resistances"].append(float(tokens[4]))
152+
layer_segments[tokens[2]]["capacitances"].append(float(tokens[5]))
133153
else:
134154
netName = tokens[0]
135155

@@ -350,4 +370,34 @@ def generic_rc_fit(type_sieve):
350370
################################################################
351371

352372
if args.mode == "segment":
353-
pass
373+
print(
374+
"# Updated layer resistance {}/um capacitance {}/um".format(res_unit, cap_unit)
375+
)
376+
377+
for layer_name in routing_layers:
378+
# There may be routing layers with no segments, so we check if the
379+
# layer exists in the dict.
380+
if layer_name not in layer_segments:
381+
continue
382+
383+
# sklearn requires the input to be 2D, so we reshape to add a dimension
384+
# to the list.
385+
lengths = np.array(layer_segments[layer_name]["lengths"]).reshape(-1, 1)
386+
resistances = np.array(layer_segments[layer_name]["resistances"])
387+
capacitances_ff = np.array(layer_segments[layer_name]["capacitances"])
388+
389+
res_model = LinearRegression(fit_intercept=False).fit(lengths, resistances)
390+
cap_model = LinearRegression(fit_intercept=False).fit(lengths, capacitances_ff)
391+
392+
r_sq = res_model.score(lengths, resistances)
393+
print("# Resistance coefficient of determination: {:.4f}".format(r_sq))
394+
r_sq = cap_model.score(lengths, capacitances_ff)
395+
print("# Capacitance coefficient of determination: {:.4f}".format(r_sq))
396+
397+
print(
398+
"set_layer_rc -layer {} -resistance {:.5E} -capacitance {:.5E}".format(
399+
layer_name,
400+
res_model.coef_[0] / res_scale,
401+
cap_model.coef_[0] * 1e-15 / cap_scale,
402+
)
403+
)

flow/util/utils.mk

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ write_net_rc: $(RESULTS_DIR)/6_net_rc.csv
9191
$(RESULTS_DIR)/6_net_rc.csv:
9292
($(TIME_CMD) $(OPENROAD_CMD) $(UTILS_DIR)/write_net_rc_script.tcl) 2>&1 | tee $(LOG_DIR)/6_write_net_rc.log
9393

94+
.PHONY: write_segment_rc
95+
write_segment_rc: $(RESULTS_DIR)/6_segment_rc.csv
96+
97+
$(RESULTS_DIR)/6_segment_rc.csv:
98+
($(TIME_CMD) $(OPENROAD_CMD) $(UTILS_DIR)/write_segment_rc_script.tcl) 2>&1 | tee $(LOG_DIR)/6_write_segment_rc.log
99+
94100
.PHONY: correlate_rc
95101
correlate_rc: $(RESULTS_DIR)/6_net_rc.csv
96102
$(PYTHON_EXE) $(UTILS_DIR)/correlateRC.py $(RESULTS_DIR)/6_net_rc.csv

flow/util/write_segment_rc.tcl

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# This function only works if each routing wire shape corresponds
2+
# to one parasitic resistive model.
3+
proc fetch_segments_rc { net_to_segments_var } {
4+
upvar 1 $net_to_segments_var net_to_segments
5+
6+
foreach sta_net [get_nets *] {
7+
set db_net [sta::sta_to_db_net $sta_net]
8+
set type [$db_net getSigType]
9+
10+
if { !([string equal $type "CLOCK"] || [string equal $type "SIGNAL"]) } {
11+
continue
12+
}
13+
14+
set wire [$db_net getWire]
15+
16+
if { $wire eq "NULL" } {
17+
continue
18+
}
19+
20+
set segments {}
21+
set seen_shape_ids {}
22+
foreach rseg [$db_net getRSegs] {
23+
set shape [$wire getShape [$rseg getShapeId]]
24+
25+
# We skip vias as they have no capacitance in RCX.
26+
if { ![$shape isSegment] } {
27+
continue
28+
}
29+
30+
set shape_id [$rseg getShapeId]
31+
32+
if { $shape_id in $seen_shape_ids } {
33+
error "Could not fetch segment parasitics data: shape\
34+
$shape_id on net [$db_net getName] has multiple rsegs."
35+
}
36+
37+
set layer [[$shape getTechLayer] getName]
38+
39+
set width [$shape getDX]
40+
set height [$shape getDY]
41+
set length_um [ord::dbu_to_microns [expr { max($width, $height) }]]
42+
43+
set resistance [$rseg getResistance 0]
44+
set capacitance [$rseg getCapacitance 0]
45+
46+
lappend segments $layer $length_um $resistance $capacitance
47+
lappend seen_shape_ids $shape_id
48+
}
49+
50+
set net_to_segments([get_full_name $sta_net]) $segments
51+
}
52+
}
53+
54+
proc write_segment_rc_csv { filename net_to_segments_var } {
55+
upvar 1 $net_to_segments_var net_to_segments
56+
57+
set stream [open $filename "w"]
58+
59+
# First, write a header listing the routing layers in stack order.
60+
puts -nonewline $stream "# routing layers:"
61+
foreach layer [[ord::get_db_tech] getLayers] {
62+
if { [$layer getRoutingLevel] != 0 } {
63+
puts -nonewline $stream " [$layer getName]"
64+
}
65+
}
66+
67+
puts $stream ""
68+
69+
# Then, write the parasitics data of each wire segment.
70+
foreach sta_net [get_nets *] {
71+
set net_name [get_full_name $sta_net]
72+
73+
if { ![info exists net_to_segments($net_name)] } {
74+
continue
75+
}
76+
77+
set db_net [sta::sta_to_db_net $sta_net]
78+
set type [$db_net getSigType]
79+
set net_type [expr { $type eq "CLOCK" ? "clock" : "signal" }]
80+
81+
foreach {layer length_um resistance capacitance} $net_to_segments($net_name) {
82+
puts $stream [format "%s,%s,%s,%.3e,%.3e,%.3e" \
83+
$net_name $net_type $layer $length_um $resistance $capacitance]
84+
}
85+
}
86+
87+
close $stream
88+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
source $::env(SCRIPTS_DIR)/load.tcl
2+
load_design 6_final.odb 6_final.sdc
3+
4+
source $::env(UTILS_DIR)/write_segment_rc.tcl
5+
6+
# Set up RCX parameters to avoid any parasitics segment merging.
7+
extract_parasitics -ext_model_file $::env(RCX_RULES) -max_res 0 -no_merge_via_res
8+
fetch_segments_rc rcx
9+
10+
write_segment_rc_csv $::env(RESULTS_DIR)/6_segment_rc.csv rcx

0 commit comments

Comments
 (0)