mirror of https://github.com/openXC7/prjxray.git
Make MMCM pip fuzzer more robust.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
parent
af6700a692
commit
4e4bcae418
|
|
@ -220,9 +220,6 @@ def main():
|
||||||
|
|
||||||
tags_to_remove = set()
|
tags_to_remove = set()
|
||||||
for tag, bits in segbits.items():
|
for tag, bits in segbits.items():
|
||||||
if len(bits) == 0:
|
|
||||||
tags_to_remove.add(tag)
|
|
||||||
|
|
||||||
if 'REBUF' in tag and 'ACTIVE' not in tag:
|
if 'REBUF' in tag and 'ACTIVE' not in tag:
|
||||||
# FIXME: Removing REBUF pips for now.
|
# FIXME: Removing REBUF pips for now.
|
||||||
tags_to_remove.add(tag)
|
tags_to_remove.add(tag)
|
||||||
|
|
@ -232,15 +229,14 @@ def main():
|
||||||
|
|
||||||
for tag in segbits.keys():
|
for tag in segbits.keys():
|
||||||
if tag.endswith("_ACTIVE") and 'FREQ_BB' in tag:
|
if tag.endswith("_ACTIVE") and 'FREQ_BB' in tag:
|
||||||
if 'FREQ_BB_REBUF' in tag:
|
for idx in range(4):
|
||||||
m = re.search('FREQ_BB_REBUF([0-9])', tag)
|
prefix1 = '.CMT_L_LOWER_B_CLK_FREQ_BB{}'.format(idx)
|
||||||
else:
|
prefix2 = '.CMT_R_LOWER_B_CLK_FREQ_BB{}'.format(idx)
|
||||||
m = re.search('FREQ_BB_NS([0-9])', tag)
|
tags_to_mask = [
|
||||||
assert m is not None, tag
|
t for t in segbits.keys()
|
||||||
|
if t.endswith(prefix1) or t.endswith(prefix2)
|
||||||
prefix = '.CMT_R_LOWER_B_CLK_FREQ_BB{}'.format(m.group(1))
|
]
|
||||||
tags_to_mask = [t for t in segbits.keys() if t.endswith(prefix)]
|
mask_out_bits(segbits, segbits[tag], tags_to_mask)
|
||||||
mask_out_bits(segbits, segbits[tag], tags_to_mask)
|
|
||||||
|
|
||||||
# Find common bits
|
# Find common bits
|
||||||
bit_groups = find_common_bits_for_tag_groups(segbits, tag_groups)
|
bit_groups = find_common_bits_for_tag_groups(segbits, tag_groups)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,116 @@ proc write_used_wires {filename} {
|
||||||
close $fp
|
close $fp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc load_route {line} {
|
||||||
|
puts "MANROUTE: Line: $line"
|
||||||
|
|
||||||
|
# Parse the line
|
||||||
|
set fields [split $line " "]
|
||||||
|
set tile_name [lindex $fields 0]
|
||||||
|
set site_name [lindex $fields 1]
|
||||||
|
set pin_name [lindex $fields 2]
|
||||||
|
set route_dir [lindex $fields 3]
|
||||||
|
set wires [lrange $fields 4 end]
|
||||||
|
|
||||||
|
# Get net
|
||||||
|
set tile [get_tiles $tile_name]
|
||||||
|
set site [get_sites -of_objects $tile $site_name]
|
||||||
|
set pin [get_site_pins -of_objects $site "*/$pin_name"]
|
||||||
|
set net [get_nets -quiet -of_objects $pin]
|
||||||
|
|
||||||
|
if {$net eq "" } {
|
||||||
|
puts "MANROUTE: No net for pin $pin_name found! Skipping..."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
set source_pin [get_pins -of $net -filter "DIRECTION == OUT"]
|
||||||
|
if { [llength $source_pin] == 0 } {
|
||||||
|
error "Failed to find source pin of net $net"
|
||||||
|
}
|
||||||
|
|
||||||
|
set source_site_pins [get_site_pins -of $source_pin]
|
||||||
|
puts "MANROUTE: Possible site pins for net $net: $source_site_pins"
|
||||||
|
|
||||||
|
# Fixed part read from file
|
||||||
|
set route_list {}
|
||||||
|
if { [string first / $wires] != -1 } {
|
||||||
|
# Route list is just intermediate node and final node, fill it in
|
||||||
|
if { [llength $wires] != 2} {
|
||||||
|
error "Expected only 2 wires, found [llength $wires]"
|
||||||
|
}
|
||||||
|
|
||||||
|
set intermediate_node [get_nodes [lindex $wires 0]]
|
||||||
|
set dest_node [get_nodes -of_objects [get_wires -of_objects $tile "*/[lindex $wires 1]"]]
|
||||||
|
if [catch {find_routing_path -from $intermediate_node -to $dest_node} route_list] {
|
||||||
|
puts "MANROUTE: Failed to find routing path from $intermediate_node to $dest_node for net $net\nError: $route_list"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if { [llength $route_list] == 0 } {
|
||||||
|
puts "MANROUTE: Failed to find routing path from $intermediate_node to $dest_node for net $net"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach wire $wires {
|
||||||
|
lappend route_list [get_nodes -of_objects [get_wires -of_objects $tile "*/$wire"]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Complete the route
|
||||||
|
if {$route_dir eq "up"} {
|
||||||
|
set node_to [lindex $route_list 0]
|
||||||
|
set node_from [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]]
|
||||||
|
|
||||||
|
set rpart [find_routing_path -from $node_from -to $node_to]
|
||||||
|
if {$rpart eq ""} {
|
||||||
|
puts "MANROUTE: No possible route continuation for net $net"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
set route_list [concat [lrange $rpart 0 end-1] $route_list]
|
||||||
|
}
|
||||||
|
|
||||||
|
if {$route_dir eq "down"} {
|
||||||
|
set node_from [lindex $route_list e]
|
||||||
|
set node_to [get_nodes -of_objects [get_site_pins -filter {DIRECTION == IN} -of_objects $net]]
|
||||||
|
|
||||||
|
set rpart [find_routing_path -from $node_from -to $node_to]
|
||||||
|
if {$rpart eq ""} {
|
||||||
|
puts "MANROUTE: No possible route continuation for net $net"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
set route_list [concat $route_list [lrange $rpart 1 end]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the fixed route
|
||||||
|
puts "MANROUTE: Net: $net, Route: $route_list. routing..."
|
||||||
|
regsub -all {{}} $route_list "" route
|
||||||
|
if [catch {set_property FIXED_ROUTE $route $net} result] {
|
||||||
|
puts "MANROUTE: Net $net failed to set FIXED_ROUTE, ripping up...\nError: $result"
|
||||||
|
set_property FIXED_ROUTE "" $net
|
||||||
|
set_property IS_ROUTE_FIXED 0 $net
|
||||||
|
route_design -unroute -nets $net
|
||||||
|
}
|
||||||
|
|
||||||
|
# Route the single net. Needed to detect conflicts when evaluating
|
||||||
|
# other ones
|
||||||
|
puts "Running route design"
|
||||||
|
route_design -quiet -directive Quick -nets $net
|
||||||
|
puts "Done running route design"
|
||||||
|
|
||||||
|
# Check for conflicts.
|
||||||
|
set status [get_property ROUTE_STATUS $net]
|
||||||
|
if {$status ne "ROUTED"} {
|
||||||
|
# Ripup and discard the fixed route.
|
||||||
|
set_property FIXED_ROUTE "" $net
|
||||||
|
route_design -unroute -nets $net
|
||||||
|
puts "MANROUTE: Net $net status $status, ripping up..."
|
||||||
|
} else {
|
||||||
|
set_property IS_ROUTE_FIXED 1 $net
|
||||||
|
puts "MANROUTE: Successful manual route for $net"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proc load_routes {filename} {
|
proc load_routes {filename} {
|
||||||
puts "MANROUTE: Loading routes from $filename"
|
puts "MANROUTE: Loading routes from $filename"
|
||||||
|
|
||||||
|
|
@ -56,86 +166,7 @@ proc load_routes {filename} {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
puts "MANROUTE: Line: $line"
|
load_route $line
|
||||||
|
|
||||||
# Parse the line
|
|
||||||
set fields [split $line " "]
|
|
||||||
set tile_name [lindex $fields 0]
|
|
||||||
set site_name [lindex $fields 1]
|
|
||||||
set pin_name [lindex $fields 2]
|
|
||||||
set route_dir [lindex $fields 3]
|
|
||||||
set wires [lrange $fields 4 end]
|
|
||||||
|
|
||||||
# Get net
|
|
||||||
set tile [get_tiles $tile_name]
|
|
||||||
set site [get_sites -of_objects $tile $site_name]
|
|
||||||
set pin [get_site_pins -of_objects $site "*/$pin_name"]
|
|
||||||
set net [get_nets -quiet -of_objects $pin]
|
|
||||||
|
|
||||||
if {$net eq "" } {
|
|
||||||
puts "MANROUTE: No net for pin $pin_name found! Skipping..."
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
# Fixed part read from file
|
|
||||||
set route_list {}
|
|
||||||
foreach wire $wires {
|
|
||||||
lappend route_list [get_nodes -of_objects [get_wires -of_objects $tile "*/$wire"]]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Complete the route
|
|
||||||
if {$route_dir eq "up"} {
|
|
||||||
set node_to [lindex $route_list 0]
|
|
||||||
set node_from [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]]
|
|
||||||
|
|
||||||
set rpart [find_routing_path -from $node_from -to $node_to]
|
|
||||||
if {$rpart eq ""} {
|
|
||||||
puts "MANROUTE: No possible route continuation for net $net"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
set route_list [concat [lrange $rpart 0 end-1] $route_list]
|
|
||||||
}
|
|
||||||
|
|
||||||
if {$route_dir eq "down"} {
|
|
||||||
set node_from [lindex $route_list e]
|
|
||||||
set node_to [get_nodes -of_objects [get_site_pins -filter {DIRECTION == IN} -of_objects $net]]
|
|
||||||
|
|
||||||
set rpart [find_routing_path -from $node_from -to $node_to]
|
|
||||||
if {$rpart eq ""} {
|
|
||||||
puts "MANROUTE: No possible route continuation for net $net"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
set route_list [concat $route_list [lrange $rpart 1 end]]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set the fixed route
|
|
||||||
puts "MANROUTE: Net: $net, Route: $route_list. routing..."
|
|
||||||
regsub -all {{}} $route_list "" route
|
|
||||||
if [catch {set_property FIXED_ROUTE $route $net} ] {
|
|
||||||
puts "MANROUTE: Net $net failed to set FIXED_ROUTE, ripping up..."
|
|
||||||
set_property FIXED_ROUTE "" $net
|
|
||||||
set_property IS_ROUTE_FIXED 0 $net
|
|
||||||
route_design -unroute -nets $net
|
|
||||||
}
|
|
||||||
|
|
||||||
# Route the single net. Needed to detect conflicts when evaluating
|
|
||||||
# other ones
|
|
||||||
puts "Running route design"
|
|
||||||
route_design -quiet -directive Quick -nets $net
|
|
||||||
puts "Done running route design"
|
|
||||||
|
|
||||||
# Check for conflicts.
|
|
||||||
set status [get_property ROUTE_STATUS $net]
|
|
||||||
if {$status ne "ROUTED"} {
|
|
||||||
# Ripup and discard the fixed route.
|
|
||||||
set_property FIXED_ROUTE "" $net
|
|
||||||
route_design -unroute -nets $net
|
|
||||||
puts "MANROUTE: Net $net status $status, ripping up..."
|
|
||||||
} else {
|
|
||||||
set_property IS_ROUTE_FIXED 1 $net
|
|
||||||
puts "MANROUTE: Successful manual route for $net"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close $fp
|
close $fp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_IN3_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_IN3_INT
|
||||||
|
|
||||||
|
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_IN1_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_IN1_INT
|
||||||
|
|
||||||
|
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_IN2_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_IN2_INT
|
||||||
|
|
||||||
|
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_IN3_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_IN3_INT
|
||||||
|
|
||||||
|
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_IN1_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_IN1_INT
|
||||||
|
|
||||||
|
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_IN2_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_IN2_INT
|
||||||
|
|
@ -84,6 +84,20 @@ def find_phasers_for_mmcm(grid, loc):
|
||||||
return phasers
|
return phasers
|
||||||
|
|
||||||
|
|
||||||
|
def find_hclk_ref_wires_for_mmcm(grid, loc):
|
||||||
|
tilename = grid.tilename_at_loc((loc[0], loc[1] - 17))
|
||||||
|
gridinfo = grid.gridinfo_at_tilename(tilename)
|
||||||
|
|
||||||
|
assert gridinfo.tile_type in ['HCLK_CMT_L', 'HCLK_CMT']
|
||||||
|
|
||||||
|
# HCLK_CMT_MUX_OUT_FREQ_REF[0-3]
|
||||||
|
wires = []
|
||||||
|
for idx in range(4):
|
||||||
|
wires.append('{}/HCLK_CMT_MUX_OUT_FREQ_REF{}'.format(tilename, idx))
|
||||||
|
|
||||||
|
return wires
|
||||||
|
|
||||||
|
|
||||||
def gen_sites():
|
def gen_sites():
|
||||||
db = Database(util.get_db_root(), util.get_part())
|
db = Database(util.get_db_root(), util.get_part())
|
||||||
grid = db.grid()
|
grid = db.grid()
|
||||||
|
|
@ -94,11 +108,12 @@ def gen_sites():
|
||||||
for site_name, site_type in gridinfo.sites.items():
|
for site_name, site_type in gridinfo.sites.items():
|
||||||
if site_type in ['MMCME2_ADV']:
|
if site_type in ['MMCME2_ADV']:
|
||||||
phasers = find_phasers_for_mmcm(grid, loc)
|
phasers = find_phasers_for_mmcm(grid, loc)
|
||||||
yield tile_name, site_name, phasers
|
hclk_wires = find_hclk_ref_wires_for_mmcm(grid, loc)
|
||||||
|
yield tile_name, site_name, phasers, hclk_wires
|
||||||
|
|
||||||
|
|
||||||
def get_random_route_from_site_pin(
|
def get_random_route_from_site_pin(
|
||||||
pip_list, tile_name, site_pin, direction, occupied_wires):
|
pip_list, tile_name, site_pin, direction, occupied_wires, hclk_wires):
|
||||||
|
|
||||||
# A map of MMCM site pins to wires they are connected to.
|
# A map of MMCM site pins to wires they are connected to.
|
||||||
pin_to_wire = {
|
pin_to_wire = {
|
||||||
|
|
@ -138,6 +153,9 @@ def get_random_route_from_site_pin(
|
||||||
# The first wire
|
# The first wire
|
||||||
wire = pin_to_wire[tile_type][site_pin]
|
wire = pin_to_wire[tile_type][site_pin]
|
||||||
|
|
||||||
|
if site_pin in ["CLKIN1", "CLKIN2", "CLKFBIN"] and random.random() < .2:
|
||||||
|
return [random.choice(hclk_wires), wire]
|
||||||
|
|
||||||
# Walk randomly.
|
# Walk randomly.
|
||||||
route = []
|
route = []
|
||||||
while True:
|
while True:
|
||||||
|
|
@ -199,7 +217,7 @@ module top(
|
||||||
count = 0
|
count = 0
|
||||||
mmcms = sorted(gen_sites(), key=lambda x: x[0])
|
mmcms = sorted(gen_sites(), key=lambda x: x[0])
|
||||||
random.shuffle(mmcms)
|
random.shuffle(mmcms)
|
||||||
for tile, site, phasers in mmcms:
|
for tile, site, phasers, hclk_wires in mmcms:
|
||||||
in_use = random.randint(0, 2) > 0
|
in_use = random.randint(0, 2) > 0
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
|
|
@ -219,20 +237,20 @@ module top(
|
||||||
|
|
||||||
# Sometimes manually randomized route for CLKOUTx conflicts with
|
# Sometimes manually randomized route for CLKOUTx conflicts with
|
||||||
# the verilog design.
|
# the verilog design.
|
||||||
('CLKOUT0', 'down'),
|
#('CLKOUT0', 'down'),
|
||||||
('CLKOUT1', 'down'),
|
#('CLKOUT1', 'down'),
|
||||||
('CLKOUT2', 'down'),
|
#('CLKOUT2', 'down'),
|
||||||
('CLKOUT3', 'down'),
|
#('CLKOUT3', 'down'),
|
||||||
('CLKOUT4', 'down'),
|
#('CLKOUT4', 'down'),
|
||||||
('CLKOUT5', 'down'),
|
#('CLKOUT5', 'down'),
|
||||||
('CLKOUT6', 'down'),
|
#('CLKOUT6', 'down'),
|
||||||
]
|
]
|
||||||
|
|
||||||
occupied_wires = set()
|
occupied_wires = set()
|
||||||
for pin, dir in pins:
|
for pin, dir in pins:
|
||||||
|
|
||||||
route = get_random_route_from_site_pin(
|
route = get_random_route_from_site_pin(
|
||||||
pip_list, tile, pin, dir, occupied_wires)
|
pip_list, tile, pin, dir, occupied_wires, hclk_wires)
|
||||||
if route is None:
|
if route is None:
|
||||||
endpoints[pin] = ""
|
endpoints[pin] = ""
|
||||||
continue
|
continue
|
||||||
|
|
@ -248,11 +266,10 @@ module top(
|
||||||
# Store them in a random order so the TCL script will try to route
|
# Store them in a random order so the TCL script will try to route
|
||||||
# them also in the random order.
|
# them also in the random order.
|
||||||
lines = []
|
lines = []
|
||||||
for pin, (
|
pins = sorted(routes.keys())
|
||||||
route,
|
random.shuffle(pins)
|
||||||
dir,
|
for pin in pins:
|
||||||
) in routes.items():
|
(route, dir) = routes[pin]
|
||||||
|
|
||||||
route_str = " ".join(route)
|
route_str = " ".join(route)
|
||||||
lines.append(
|
lines.append(
|
||||||
'{} {} {} {} {}\n'.format(tile, site, pin, dir, route_str))
|
'{} {} {} {} {}\n'.format(tile, site, pin, dir, route_str))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue