Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 52 additions & 2 deletions flow/util/correlateRC.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ def makeDict():
stack = []
stack_line = None

# For segment mode.
layer_segments = defaultdict(
lambda: {"lengths": [], "resistances": [], "capacitances": []}
)
routing_layers = []
routing_layers_line = None

# indices of relevant layers (routable layers or via layers)
active_layers = set()

Expand All @@ -104,6 +111,17 @@ def makeDict():
with open(rc_file) as f:
nonGrtNets = 0
for line in f:
if line.startswith("# routing layers: "):
if routing_layers_line is not None and routing_layers_line != line:
print(f"layer stack inconsistent", file=stderr)
exit(1)
elif routing_layers_line is None:
routing_layers = (
line.removeprefix("# routing layers: ").strip().split(" ")
)
routing_layers_line = line
continue

if line.startswith("# stack: "):
if stack_line is not None and stack_line != line:
print(f"layer stack inconsistent", file=stderr)
Expand All @@ -129,7 +147,9 @@ def makeDict():
tokens = line.strip().split(",")

if args.mode == "segment":
pass
layer_segments[tokens[2]]["lengths"].append(float(tokens[3]))
layer_segments[tokens[2]]["resistances"].append(float(tokens[4]))
layer_segments[tokens[2]]["capacitances"].append(float(tokens[5]))
else:
netName = tokens[0]

Expand Down Expand Up @@ -350,4 +370,34 @@ def generic_rc_fit(type_sieve):
################################################################

if args.mode == "segment":
pass
print(
"# Updated layer resistance {}/um capacitance {}/um".format(res_unit, cap_unit)
)

for layer_name in routing_layers:
# There may be routing layers with no segments, so we check if the
# layer exists in the dict.
if layer_name not in layer_segments:
continue

# sklearn requires the input to be 2D, so we reshape to add a dimension
# to the list.
lengths = np.array(layer_segments[layer_name]["lengths"]).reshape(-1, 1)
resistances = np.array(layer_segments[layer_name]["resistances"])
capacitances_ff = np.array(layer_segments[layer_name]["capacitances"])

res_model = LinearRegression(fit_intercept=False).fit(lengths, resistances)
cap_model = LinearRegression(fit_intercept=False).fit(lengths, capacitances_ff)

r_sq = res_model.score(lengths, resistances)
print("# Resistance coefficient of determination: {:.4f}".format(r_sq))
r_sq = cap_model.score(lengths, capacitances_ff)
print("# Capacitance coefficient of determination: {:.4f}".format(r_sq))

print(
"set_layer_rc -layer {} -resistance {:.5E} -capacitance {:.5E}".format(
layer_name,
res_model.coef_[0] / res_scale,
cap_model.coef_[0] * 1e-15 / cap_scale,
)
)
6 changes: 6 additions & 0 deletions flow/util/utils.mk
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ write_net_rc: $(RESULTS_DIR)/6_net_rc.csv
$(RESULTS_DIR)/6_net_rc.csv:
($(TIME_CMD) $(OPENROAD_CMD) $(UTILS_DIR)/write_net_rc_script.tcl) 2>&1 | tee $(LOG_DIR)/6_write_net_rc.log

.PHONY: write_segment_rc
write_segment_rc: $(RESULTS_DIR)/6_segment_rc.csv

$(RESULTS_DIR)/6_segment_rc.csv:
($(TIME_CMD) $(OPENROAD_CMD) $(UTILS_DIR)/write_segment_rc_script.tcl) 2>&1 | tee $(LOG_DIR)/6_write_segment_rc.log

.PHONY: correlate_rc
correlate_rc: $(RESULTS_DIR)/6_net_rc.csv
$(PYTHON_EXE) $(UTILS_DIR)/correlateRC.py $(RESULTS_DIR)/6_net_rc.csv
Expand Down
88 changes: 88 additions & 0 deletions flow/util/write_segment_rc.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# This function only works if each routing wire shape corresponds
# to one parasitic resistive model.
proc fetch_segments_rc { net_to_segments_var } {
upvar 1 $net_to_segments_var net_to_segments

foreach sta_net [get_nets *] {
set db_net [sta::sta_to_db_net $sta_net]
set type [$db_net getSigType]

if { !([string equal $type "CLOCK"] || [string equal $type "SIGNAL"]) } {
continue
}

set wire [$db_net getWire]

if { $wire eq "NULL" } {
continue
}

set segments {}
set seen_shape_ids {}
foreach rseg [$db_net getRSegs] {
set shape [$wire getShape [$rseg getShapeId]]

# We skip vias as they have no capacitance in RCX.
if { ![$shape isSegment] } {
continue
}

set shape_id [$rseg getShapeId]

if { $shape_id in $seen_shape_ids } {
error "Could not fetch segment parasitics data: shape\
$shape_id on net [$db_net getName] has multiple rsegs."
}

set layer [[$shape getTechLayer] getName]

set width [$shape getDX]
set height [$shape getDY]
set length_um [ord::dbu_to_microns [expr { max($width, $height) }]]

set resistance [$rseg getResistance 0]
set capacitance [$rseg getCapacitance 0]

lappend segments $layer $length_um $resistance $capacitance
lappend seen_shape_ids $shape_id
}

set net_to_segments([get_full_name $sta_net]) $segments
}
}

proc write_segment_rc_csv { filename net_to_segments_var } {
upvar 1 $net_to_segments_var net_to_segments

set stream [open $filename "w"]

# First, write a header listing the routing layers in stack order.
puts -nonewline $stream "# routing layers:"
foreach layer [[ord::get_db_tech] getLayers] {
if { [$layer getRoutingLevel] != 0 } {
puts -nonewline $stream " [$layer getName]"
}
}

puts $stream ""

# Then, write the parasitics data of each wire segment.
foreach sta_net [get_nets *] {
set net_name [get_full_name $sta_net]

if { ![info exists net_to_segments($net_name)] } {
continue
}

set db_net [sta::sta_to_db_net $sta_net]
set type [$db_net getSigType]
set net_type [expr { $type eq "CLOCK" ? "clock" : "signal" }]

foreach {layer length_um resistance capacitance} $net_to_segments($net_name) {
puts $stream [format "%s,%s,%s,%.3e,%.3e,%.3e" \
$net_name $net_type $layer $length_um $resistance $capacitance]
}
}

close $stream
}
10 changes: 10 additions & 0 deletions flow/util/write_segment_rc_script.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
source $::env(SCRIPTS_DIR)/load.tcl
load_design 6_final.odb 6_final.sdc

source $::env(UTILS_DIR)/write_segment_rc.tcl

# Set up RCX parameters to avoid any parasitics segment merging.
extract_parasitics -ext_model_file $::env(RCX_RULES) -max_res 0 -no_merge_via_res
fetch_segments_rc rcx

write_segment_rc_csv $::env(RESULTS_DIR)/6_segment_rc.csv rcx