Good macro configs from 3cpu-512w-nobuff-wrapped.
From commit 4e60cd8416751ad0bf38814006a930c58a6f0468
diff --git a/openlane/user_proj_example/config.tcl b/openlane/user_proj_example/config.tcl
index 4e5cc61..a50398d 100644
--- a/openlane/user_proj_example/config.tcl
+++ b/openlane/user_proj_example/config.tcl
@@ -1,41 +1,57 @@
-# SPDX-FileCopyrightText: 2020 Efabless Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# SPDX-License-Identifier: Apache-2.0
-
set script_dir [file dirname [file normalize [info script]]]
set ::env(DESIGN_NAME) user_proj_example
set ::env(VERILOG_FILES) "\
$script_dir/../../verilog/rtl/defines.v \
- $script_dir/../../verilog/rtl/user_proj_example.v"
+ $script_dir/../../verilog/rtl/user_proj_example.v \
+ $script_dir/../../verilog/rtl/softshell/rtl/softshell_top.v \
+ $script_dir/../../verilog/rtl/softshell/rtl/rv_core.v \
+ $script_dir/../../verilog/rtl/softshell/rtl/pinmux.v \
+ $script_dir/../../verilog/rtl/softshell/rtl/pcpi_flexio.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/verilog-wishbone/rtl/wb_arbiter_3.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/verilog-wishbone/rtl/wb_arbiter_4.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/verilog-wishbone/rtl/wb_arbiter_5.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/verilog-wishbone/rtl/arbiter.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/verilog-wishbone/rtl/priority_encoder.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/verilog-wishbone/rtl/wb_mux_3.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/verilog-wishbone/rtl/wb_mux_5.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/picorv32_wb/mem_ff_wb.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/picorv32_wb/simpleuart.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/picorv32_wb/spimemio.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/picorv32_wb/gpio32_wb.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/picorv32_wb/picorv32.v \
+ $script_dir/../../verilog/rtl/softshell/third_party/wb2axip/rtl/afifo.v"
-set ::env(CLOCK_PORT) ""
-set ::env(CLOCK_NET) "counter.clk"
-set ::env(CLOCK_PERIOD) "10"
+#set ::env(VERILOG_INCLUDE_DIRS) "\
+# $script_dir/../../softshell"
+
+# For the manually instantiated buffers in softshell_top.
+# set ::env(SYNTH_READ_BLACKBOX_LIB) 1
+
+set ::env(CLOCK_PORT) "wb_clk_i"
+#set ::env(CLOCK_NET) "softshell.wb_clk_i"
+set ::env(CLOCK_PERIOD) "20"
set ::env(FP_SIZING) absolute
-set ::env(DIE_AREA) "0 0 600 600"
+set ::env(DIE_AREA) "0 0 2500 3100"
set ::env(DESIGN_IS_CORE) 0
+set ::env(FP_PDN_CORE_RING) 0
-set ::env(VDD_NETS) [list {vccd1} {vccd2} {vdda1} {vdda2}]
-set ::env(GND_NETS) [list {vssd1} {vssd2} {vssa1} {vssa2}]
+#set ::env(GLB_RT_ALLOW_CONGESTION) 1
+set ::env(GLB_RT_MAXLAYER) 5
+
+# 0.4 results in shorts, 0.35 - 0.3 sometimes results in metal loops due to routing.
+set ::env(PL_TARGET_DENSITY) 0.38
+
+# Don't use met5.
+set ::env(GLB_RT_OBS) "met5 0 0 2500 3100"
+
+# Diodes inserted using interactive.tcl.
+set ::env(DIODE_INSERTION_STRATEGY) 0
+
+set ::env(ROUTING_CORES) 6
set ::env(FP_PIN_ORDER_CFG) $script_dir/pin_order.cfg
-
-set ::env(PL_BASIC_PLACEMENT) 1
-set ::env(PL_TARGET_DENSITY) 0.15
-
-# If you're going to use multiple power domains, then keep this disabled.
-set ::env(RUN_CVC) 0
+# set ::env(FP_CONTEXT_DEF) $script_dir/../user_project_wrapper/runs/user_project_wrapper/tmp/floorplan/ioPlacer.def.macro_placement.def
+# set ::env(FP_CONTEXT_LEF) $script_dir/../user_project_wrapper/runs/user_project_wrapper/tmp/merged_unpadded.lef
diff --git a/openlane/user_proj_example/copy_results.sh b/openlane/user_proj_example/copy_results.sh
new file mode 100755
index 0000000..d31feab
--- /dev/null
+++ b/openlane/user_proj_example/copy_results.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+set -x
+
+IN_PATH="./$1"
+OUT_PATH="../.."
+ARTIFACT="user_proj_example"
+
+echo "Copying results from '${IN_PATH}' to '${OUT_PATH}'"
+
+cp -pf "${IN_PATH}/results/routing/${ARTIFACT}.def" "${OUT_PATH}/def/${ARTIFACT}.def"
+cp -pf "${IN_PATH}/results/magic/${ARTIFACT}.gds" "${OUT_PATH}/gds/${ARTIFACT}.gds"
+cp -pf "${IN_PATH}/results/magic/${ARTIFACT}.lef" "${OUT_PATH}/lef/${ARTIFACT}.lef"
+cp -pf "${IN_PATH}/results/magic/${ARTIFACT}.mag" "${OUT_PATH}/mag/${ARTIFACT}.mag"
+cp -pf "${IN_PATH}/results/lvs/${ARTIFACT}.lvs.powered.v" "${OUT_PATH}/verilog/gl/${ARTIFACT}.v"
+cp -pf "${IN_PATH}/results/magic/${ARTIFACT}.spice" "${OUT_PATH}/spi/lvs/${ARTIFACT}.spice"
+
+echo "Copying summary"
+cp -pf "${IN_PATH}/reports/final_summary_report.csv" "${OUT_PATH}/openlane/${ARTIFACT}/"
+
+# echo "Removing old results folder and copying all results..."
+# rm -rf "${OUT_PATH}/runs/${ARTIFACT}/*"
+# cp -prf "${IN_PATH}/*" "${OUT_PATH}/openlane/${ARTIFACT}/runs/${ARTIFACT}"
+
+echo "Done"
diff --git a/openlane/user_proj_example/interactive.tcl b/openlane/user_proj_example/interactive.tcl
new file mode 100644
index 0000000..d8888ae
--- /dev/null
+++ b/openlane/user_proj_example/interactive.tcl
@@ -0,0 +1,104 @@
+#!/usr/bin/tclsh
+# Copyright 2020 Efabless Corporation
+# Copyright 2020 Sylvain Munaut
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+package require openlane;
+
+proc insert_diode {args} {
+ puts_info "Insert diodes..."
+
+ # Custom insertion script
+ set ::env(SAVE_DEF) $::env(TMP_DIR)/placement/$::env(DESIGN_NAME).diodes.def
+ try_catch python3 $::env(DESIGN_DIR)/scripts/place_diodes.py -l $::env(MERGED_LEF) -id $::env(CURRENT_DEF) -o $::env(SAVE_DEF) |& tee $::env(TERMINAL_OUTPUT) $::env(LOG_DIR)/diodes.log
+ set_def $::env(SAVE_DEF)
+
+ # Legalize
+ detailed_placement
+}
+
+proc run_flow {args} {
+ puts_info "Starting custom interactive flow..."
+
+ set script_dir [file dirname [file normalize [info script]]]
+ set options {
+ {-save_path optional}
+ {-tag optional}
+ }
+ set flags {-save}
+ parse_key_args "run_flow" args arg_values $options flags_map $flags -no_consume
+
+ prep -design $script_dir {*}$args
+
+ run_synthesis
+ run_floorplan
+ run_placement
+ run_cts
+ insert_diode
+ #gen_pdn
+ run_routing
+
+ if { $::env(DIODE_INSERTION_STRATEGY) == 2 } {
+ run_antenna_check
+ heal_antenna_violators; # modifies the routed DEF
+ }
+
+ if { $::env(LVS_INSERT_POWER_PINS) } {
+ write_powered_verilog
+ set_netlist $::env(lvs_result_file_tag).powered.v
+ }
+
+ run_magic
+
+ run_magic_spice_export
+
+ if { [info exists flags_map(-save) ] } {
+ if { [info exists arg_values(-save_path)] } {
+ save_views -lef_path $::env(magic_result_file_tag).lef \
+ -def_path $::env(tritonRoute_result_file_tag).def \
+ -gds_path $::env(magic_result_file_tag).gds \
+ -mag_path $::env(magic_result_file_tag).mag \
+ -spice_path $::env(magic_result_file_tag).spice \
+ -verilog_path $::env(CURRENT_NETLIST) \
+ -save_path $arg_values(-save_path) \
+ -tag $::env(RUN_TAG)
+ } else {
+ save_views -lef_path $::env(magic_result_file_tag).lef \
+ -def_path $::env(tritonRoute_result_file_tag).def \
+ -mag_path $::env(magic_result_file_tag).mag \
+ -gds_path $::env(magic_result_file_tag).gds \
+ -spice_path $::env(magic_result_file_tag).spice \
+ -verilog_path $::env(CURRENT_NETLIST) \
+ -tag $::env(RUN_TAG)
+ }
+ }
+
+ # Physical verification
+
+ run_magic_drc
+
+ run_lvs; # requires run_magic_spice_export
+
+ run_antenna_check
+
+ generate_final_summary_report
+
+ # Also run magic antenna check to compare (default uses OR antenna check).
+ run_magic_antenna_check
+
+ puts_success "Flow Completed Without Fatal Errors."
+}
+
+run_flow {*}$argv
diff --git a/openlane/user_proj_example/scripts/place_diodes.py b/openlane/user_proj_example/scripts/place_diodes.py
new file mode 100644
index 0000000..1e810ca
--- /dev/null
+++ b/openlane/user_proj_example/scripts/place_diodes.py
@@ -0,0 +1,341 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com>
+# SPDX-License-Identifier: Apache-2.0
+#
+
+import argparse
+import random
+import sys
+
+import opendbpy as odb
+
+
+class DiodeInserter:
+
+ def __init__(self, block, diode_cell, diode_pin, fake_diode_cell=None, side_strategy='source', short_span=0, port_protect=[], verbose=False):
+ self.block = block
+ self.verbose = verbose
+
+ self.diode_cell = diode_cell
+ self.diode_pin = diode_pin
+ self.fake_diode_cell = fake_diode_cell
+ self.side_strategy = side_strategy
+ self.short_span = short_span
+ self.port_protect = port_protect
+
+ self.true_diode_master = block.getDataBase().findMaster(diode_cell)
+ self.fake_diode_master = block.getDataBase().findMaster(fake_diode_cell) if (fake_diode_cell is not None) else None
+ if not self.check():
+ raise RuntimeError('True and Fake diodes are inconsistent')
+
+ self.diode_master = self.fake_diode_master or self.true_diode_master
+ self.diode_site = self.true_diode_master.getSite().getConstName()
+
+ self.inserted = {}
+
+ def check(self):
+ if self.fake_diode_master is None:
+ return True
+
+ tm = self.true_diode_master
+ fm = self.fake_diode_master
+
+ if fm.getSite() is None:
+ self.error("[!] Fake diode cell missing SITE attribute");x
+ else:
+ if fm.getSite().getConstName() != tm.getSite().getConstName():
+ return False
+
+ if fm.getWidth() != tm.getWidth():
+ return False
+ if fm.getHeight() != tm.getHeight():
+ return False
+
+ return True
+
+ def debug(self, msg):
+ if self.verbose:
+ print(msg, file=sys.stderr)
+
+ def error(self, msg):
+ print(msg, file=sys.stderr)
+
+ def net_source(self, net):
+ # See if it's an input pad
+ for bt in net.getBTerms():
+ if bt.getIoType() != 'INPUT':
+ continue
+ good, x, y = bt.getFirstPinLocation()
+ if good:
+ return (x, y)
+
+ # Or maybe output of a cell
+ x = odb.new_int(0)
+ y = odb.new_int(0)
+
+ for it in net.getITerms():
+ if not it.isOutputSignal():
+ continue
+ if it.getAvgXY(x,y):
+ return ( odb.get_int(x), odb.get_int(y) )
+
+ # Nothing found
+ return None
+
+ def net_from_pin(self, net, io_types=None):
+ for bt in net.getBTerms():
+ if (io_types is None) or (bt.getIoType() in io_types):
+ return True
+ return False
+
+ def net_has_diode(self, net):
+ for it in net.getITerms():
+ cell_type = it.getInst().getMaster().getConstName()
+ cell_pin = it.getMTerm().getConstName()
+ if (cell_type == self.diode_cell) and (cell_pin == self.diode_pin):
+ return True
+ else:
+ return False
+
+ def net_span(self, net):
+ xs = []
+ ys = []
+
+ for bt in net.getBTerms():
+ good, x, y = bt.getFirstPinLocation()
+ if good:
+ xs.append(x)
+ ys.append(y)
+
+ for it in net.getITerms():
+ x, y = self.pin_position(it)
+ xs.append(x)
+ ys.append(y)
+
+ if len(xs) == 0:
+ return 0
+
+ return (max(ys) - min(ys)) + (max(xs) - min(xs))
+
+ def pin_position(self, it):
+ px = odb.new_int(0)
+ py = odb.new_int(0)
+
+ if it.getAvgXY(px,py):
+ # Got it
+ return odb.get_int(px), odb.get_int(py)
+ else:
+ # Failed, use the center coordinate of the instance as fall back
+ return it.getInst().getLocation()
+
+ def place_diode_stdcell(self, it, px, py, src_pos=None):
+ # Get information about the instance
+ inst_name = it.getInst().getConstName()
+ inst_width = it.getInst().getMaster().getWidth()
+ inst_pos = it.getInst().getLocation()
+ inst_ori = it.getInst().getOrient()
+
+ # Is the pin left-ish, center-ish or right-ish ?
+ pos = None
+
+ if self.side_strategy == 'source':
+ # Always be on the side of the source
+ if src_pos is not None:
+ pos = 'l' if (src_pos[0] < inst_pos[0]) else 'r'
+
+ elif self.side_strategy == 'pin':
+ # Always be on the side of the pin
+ pos = 'l' if (px < (inst_pos[0] + inst_width // 2)) else 'r'
+
+ elif self.side_strategy == 'balanced':
+ # If pin is really on the side, use that, else use source side
+ th_left = int(inst_pos[0] + inst_width * 0.25)
+ th_right = int(inst_pos[0] + inst_width * 0.75)
+
+ if px < th_left:
+ pos = 'l'
+ elif px > th_right:
+ pos = 'r'
+ elif src_pos is not None:
+ # Sort of middle, so put it on the side where signal is coming from
+ pos = 'l' if (src_pos[0] < inst_pos[0]) else 'r'
+
+ if pos is None:
+ # Coin toss ...
+ pos = 'l' if (random.random() > 0.5) else 'r'
+
+ # X position
+ dw = self.diode_master.getWidth()
+
+ if pos == 'l':
+ dx = inst_pos[0] - dw * (1 + self.inserted.get((inst_name, 'l'), 0))
+ else:
+ dx = inst_pos[0] + inst_width + dw * self.inserted.get((inst_name, 'r'), 0)
+
+ # Record insertion
+ self.inserted[(inst_name, pos)] = self.inserted.get((inst_name, pos), 0) + 1
+
+ # Done
+ return dx, inst_pos[1], inst_ori
+
+ def place_diode_macro(self, it, px, py, src_pos=None):
+ # Scan all rows to see how close we can get to the point
+ best = None
+
+ for row in self.block.getRows():
+ rbb = row.getBBox()
+
+ dx = max(min(rbb.xMax(), px), rbb.xMin())
+ dy = rbb.yMin()
+ do = row.getOrient()
+
+ d = abs(px - dx) + abs(py - dy)
+
+ if (best is None) or (best[0] > d):
+ best = (d, dx, dy, do)
+
+ return best[1:]
+
+ def insert_diode(self, it, src_pos, force_true=False):
+ # Get information about the instance
+ inst = it.getInst()
+ inst_cell = inst.getMaster().getConstName()
+ inst_name = inst.getConstName()
+ inst_pos = inst.getLocation()
+ inst_site = inst.getMaster().getSite().getConstName() if (inst.getMaster().getSite() is not None) else None
+
+ # Find where the pin is
+ px, py = self.pin_position(it)
+
+ # Apply standard cell or macro placement ?
+ if inst_site == self.diode_site:
+ dx, dy, do = self.place_diode_stdcell(it, px, py, src_pos)
+ else:
+ dx, dy, do = self.place_diode_macro(it, px, py, src_pos)
+
+ # Insert instance and wire it up
+ diode_inst_name = 'ANTENNA_' + inst_name + '_' + it.getMTerm().getConstName()
+ diode_master = self.true_diode_master if force_true else self.diode_master
+
+ diode_inst = odb.dbInst_create(self.block, diode_master, diode_inst_name)
+
+ diode_inst.setOrient(do)
+ diode_inst.setLocation(dx, dy)
+ diode_inst.setPlacementStatus('PLACED')
+
+ ait = diode_inst.findITerm(self.diode_pin)
+ odb.dbITerm_connect(ait, it.getNet())
+
+ def execute(self):
+ # Scan all nets
+ for net in self.block.getNets():
+ # Skip special nets
+ if net.isSpecial():
+ self.debug(f"[d] Skipping special net {net.getConstName():s}")
+ continue
+
+ # Check if we already have diode on the net
+ # if yes, then we assume that the user took care of that net manually
+ if self.net_has_diode(net):
+ self.debug(f"[d] Skipping manually protected net {net.getConstName():s}")
+ continue
+
+ # Find signal source (first one found ...)
+ src_pos = self.net_source(net)
+
+ # Is this an IO we need to protect
+ io_protect = self.net_from_pin(net, io_types=self.port_protect)
+ if io_protect:
+ self.debug(f"[d] Forcing protection diode on net {net.getConstName():s}")
+
+ # Determine the span of the signal and skip small internal nets
+ span = self.net_span(net)
+ if (span < self.short_span) and not io_protect:
+ self.debug(f"[d] Skipping small net {net.getConstName():s} ({span:d})")
+ continue
+
+ # Scan all internal terminals
+ for it in net.getITerms():
+ if it.isInputSignal():
+ self.insert_diode(it, src_pos, force_true=io_protect)
+
+
+# Arguments
+parser = argparse.ArgumentParser(
+ description='Diode Insertion script')
+
+parser.add_argument('--lef', '-l',
+ nargs='+',
+ type=str,
+ default=None,
+ required=True,
+ help='Input LEF file(s)')
+
+parser.add_argument('--input-def', '-id', required=True,
+ help='DEF view of the design that needs to have diodes inserted')
+
+parser.add_argument('--output-def', '-o', required=True,
+ help='Output DEF file')
+
+parser.add_argument('--verbose', '-v', action="store_true", default=False,
+ help='Enable verbose debug output')
+
+parser.add_argument('--diode-cell', '-c', default='sky130_fd_sc_hd__diode_2',
+ help='Name of the cell to use as diode')
+
+parser.add_argument('--fake-diode-cell', '-f',
+ help='Name of the cell to use as fake diode')
+
+parser.add_argument('--diode-pin', '-p', default='DIODE',
+ help='Name of the pin to use on diode cells')
+
+parser.add_argument('--side-strategy', choices=['source', 'pin', 'balanced', 'random'], default='source',
+ help='Strategy to select if placing diode left/right of the cell')
+
+parser.add_argument('--short-span', '-s', default=90000, type=int,
+ help='Maximum span of a net to be considered "short" and not needing a diode')
+
+parser.add_argument('--port-protect', choices=['none', 'in', 'out', 'both'], default='in',
+ help='Always place a true diode on nets connected to selected ports')
+
+
+
+args = parser.parse_args()
+input_lef_file_names = args.lef
+input_def_file_name = args.input_def
+output_def_file_name = args.output_def
+
+# Load
+db_design = odb.dbDatabase.create()
+
+for lef in input_lef_file_names:
+ odb.read_lef(db_design, lef)
+odb.read_def(db_design, input_def_file_name)
+
+chip_design = db_design.getChip()
+block_design = chip_design.getBlock()
+top_design_name = block_design.getConstName()
+print("Design name:", top_design_name)
+
+
+pp_val = {
+ 'none': [],
+ 'in': ['INPUT'],
+ 'out': ['OUTPUT'],
+ 'both': ['INPUT', 'OUTPUT'],
+}
+
+di = DiodeInserter(block_design,
+ diode_cell = args.diode_cell,
+ diode_pin = args.diode_pin,
+ fake_diode_cell = args.fake_diode_cell,
+ side_strategy = args.side_strategy,
+ short_span = args.short_span,
+ port_protect = pp_val[args.port_protect],
+ verbose = args.verbose
+)
+di.execute()
+
+# Write result
+odb.write_def(block_design, output_def_file_name)
diff --git a/openlane/user_project_wrapper/config.tcl b/openlane/user_project_wrapper/config.tcl
index e60639f..dcf50f3 100644
--- a/openlane/user_project_wrapper/config.tcl
+++ b/openlane/user_project_wrapper/config.tcl
@@ -1,43 +1,49 @@
-# SPDX-FileCopyrightText: 2020 Efabless Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# SPDX-License-Identifier: Apache-2.0
-
-# Base Configurations. Don't Touch
-# section begin
set script_dir [file dirname [file normalize [info script]]]
+
set ::env(DESIGN_NAME) user_project_wrapper
-#section end
+set ::env(FP_PIN_ORDER_CFG) $script_dir/pin_order.cfg
+set ::env(PDN_CFG) $script_dir/pdn.tcl
+set ::env(FP_PDN_CORE_RING) 1
+set ::env(FP_SIZING) absolute
+set ::env(DIE_AREA) "0 0 2920 3520"
-# User Configurations
+set ::unit 2.4
+set ::env(FP_IO_VEXTEND) [expr 2*$::unit]
+set ::env(FP_IO_HEXTEND) [expr 2*$::unit]
+set ::env(FP_IO_VLENGTH) $::unit
+set ::env(FP_IO_HLENGTH) $::unit
-## Source Verilog Files
+set ::env(FP_IO_VTHICKNESS_MULT) 4
+set ::env(FP_IO_HTHICKNESS_MULT) 4
+
+set ::env(CLOCK_PORT) "wb_clk_i"
+#set ::env(CLOCK_NET) "mprj.clk"
+
+set ::env(CLOCK_PERIOD) "20"
+
+set ::env(PL_OPENPHYSYN_OPTIMIZATIONS) 0
+set ::env(DIODE_INSERTION_STRATEGY) 0
+
+set ::env(ROUTING_CORES) 6
+
+# Don't use li1 (high resistance), met4 (macro power), or met5 (power).
+set ::env(GLB_RT_MINLAYER) 2
+set ::env(GLB_RT_MAXLAYER) 4
+set ::env(GLB_RT_OBS) "li1 0 0 2920 3520, met4 0 0 2920 3520, met5 0 0 2920 3520"
+
+# Try really hard to route.
+set ::env(ROUTING_OPT_ITERS) 200
+
+# Need to fix a FastRoute bug for this to work, but it's good
+# for a sense of "isolation"
+set ::env(MAGIC_ZEROIZE_ORIGIN) 0
+set ::env(MAGIC_WRITE_FULL_LEF) 0
+
set ::env(VERILOG_FILES) "\
$script_dir/../../verilog/rtl/defines.v \
$script_dir/../../verilog/rtl/user_project_wrapper.v"
-## Clock configurations
-set ::env(CLOCK_PORT) "user_clock2"
-set ::env(CLOCK_NET) "mprj.clk"
-
-set ::env(CLOCK_PERIOD) "10"
-
-## Internal Macros
-### Macro Placement
-set ::env(MACRO_PLACEMENT_CFG) $script_dir/macro.cfg
-
-### Black-box verilog and views
set ::env(VERILOG_FILES_BLACKBOX) "\
$script_dir/../../verilog/rtl/defines.v \
$script_dir/../../verilog/rtl/user_proj_example.v"
@@ -47,60 +53,3 @@
set ::env(EXTRA_GDS_FILES) "\
$script_dir/../../gds/user_proj_example.gds"
-
-
-# The following is because there are no std cells in the example wrapper project.
-set ::env(SYNTH_TOP_LEVEL) 1
-set ::env(PL_RANDOM_GLB_PLACEMENT) 1
-set ::env(PL_OPENPHYSYN_OPTIMIZATIONS) 0
-set ::env(DIODE_INSERTION_STRATEGY) 0
-set ::env(FILL_INSERTION) 0
-set ::env(TAP_DECAP_INSERTION) 0
-set ::env(CLOCK_TREE_SYNTH) 0
-
-
-# DON'T TOUCH THE FOLLOWING SECTIONS
-
-# This makes sure that the core rings are outside the boundaries
-# of your block.
-set ::env(MAGIC_ZEROIZE_ORIGIN) 0
-
-# Area Configurations. DON'T TOUCH.
-set ::env(FP_SIZING) absolute
-set ::env(DIE_AREA) "0 0 2920 3520"
-
-# Power & Pin Configurations. DON'T TOUCH.
-set ::env(FP_PDN_CORE_RING) 1
-set ::env(FP_PDN_CORE_RING_VWIDTH) 3
-set ::env(FP_PDN_CORE_RING_HWIDTH) $::env(FP_PDN_CORE_RING_VWIDTH)
-set ::env(FP_PDN_CORE_RING_VOFFSET) 14
-set ::env(FP_PDN_CORE_RING_HOFFSET) $::env(FP_PDN_CORE_RING_VOFFSET)
-set ::env(FP_PDN_CORE_RING_VSPACING) 1.7
-set ::env(FP_PDN_CORE_RING_HSPACING) $::env(FP_PDN_CORE_RING_VSPACING)
-
-set ::env(FP_PDN_VWIDTH) 3
-set ::env(FP_PDN_HWIDTH) 3
-set ::env(FP_PDN_VOFFSET) 0
-set ::env(FP_PDN_HOFFSET) $::env(FP_PDN_VOFFSET)
-set ::env(FP_PDN_VPITCH) 180
-set ::env(FP_PDN_HPITCH) $::env(FP_PDN_VPITCH)
-set ::env(FP_PDN_VSPACING) [expr 5*$::env(FP_PDN_CORE_RING_VWIDTH)]
-set ::env(FP_PDN_HSPACING) [expr 5*$::env(FP_PDN_CORE_RING_HWIDTH)]
-
-set ::env(VDD_NETS) [list {vccd1} {vccd2} {vdda1} {vdda2}]
-set ::env(GND_NETS) [list {vssd1} {vssd2} {vssa1} {vssa2}]
-set ::env(SYNTH_USE_PG_PINS_DEFINES) "USE_POWER_PINS"
-
-set ::env(RUN_CVC) 0
-
-# Pin Configurations. DON'T TOUCH
-set ::env(FP_PIN_ORDER_CFG) $script_dir/pin_order.cfg
-set ::env(FP_DEF_TEMPLATE) $script_dir/../../def/user_project_wrapper_empty.def
-set ::unit 2.4
-set ::env(FP_IO_VEXTEND) [expr 2*$::unit]
-set ::env(FP_IO_HEXTEND) [expr 2*$::unit]
-set ::env(FP_IO_VLENGTH) $::unit
-set ::env(FP_IO_HLENGTH) $::unit
-
-set ::env(FP_IO_VTHICKNESS_MULT) 4
-set ::env(FP_IO_HTHICKNESS_MULT) 4
diff --git a/openlane/user_project_wrapper/gen_pdn.tcl b/openlane/user_project_wrapper/gen_pdn.tcl
new file mode 100644
index 0000000..9320fef
--- /dev/null
+++ b/openlane/user_project_wrapper/gen_pdn.tcl
@@ -0,0 +1,38 @@
+read_lef $::env(MERGED_LEF_UNPADDED)
+read_def $::env(CURRENT_DEF)
+
+set ::env(_SPACING) 1.7
+set ::env(_WIDTH) 3
+
+set power_domains [list {vccd1 vssd1} {vccd2 vssd2} {vdda1 vssa1} {vdda2 vssa2}]
+
+set ::env(_VDD_NET_NAME) vccd1
+set ::env(_GND_NET_NAME) vssd1
+
+set ::env(_V_OFFSET) 14
+set ::env(_H_OFFSET) $::env(_V_OFFSET)
+set ::env(_V_PITCH) 180
+set ::env(_H_PITCH) 180
+set ::env(_V_PDN_OFFSET) 0
+set ::env(_H_PDN_OFFSET) 0
+
+set ::env(CONNECT_GRIDS) 1
+
+foreach domain $power_domains {
+ set ::env(_VDD_NET_NAME) [lindex $domain 0]
+ set ::env(_GND_NET_NAME) [lindex $domain 1]
+
+ pdngen $::env(PDN_CFG) -verbose
+
+ # This is a hack to connect the power straps only for the first domain.
+ set ::env(CONNECT_GRIDS) 0
+
+ set ::env(_V_OFFSET) \
+ [expr $::env(_V_OFFSET) + 2*($::env(_WIDTH)+$::env(_SPACING))]
+ set ::env(_H_OFFSET) \
+ [expr $::env(_H_OFFSET) + 2*($::env(_WIDTH)+$::env(_SPACING))]
+ set ::env(_V_PDN_OFFSET) [expr $::env(_V_PDN_OFFSET)+6*$::env(_WIDTH)]
+ set ::env(_H_PDN_OFFSET) [expr $::env(_H_PDN_OFFSET)+6*$::env(_WIDTH)]
+}
+
+write_def $::env(pdn_tmp_file_tag).def
diff --git a/openlane/user_project_wrapper/interactive.tcl b/openlane/user_project_wrapper/interactive.tcl
new file mode 100644
index 0000000..0d0c354
--- /dev/null
+++ b/openlane/user_project_wrapper/interactive.tcl
@@ -0,0 +1,41 @@
+package require openlane
+set script_dir [file dirname [file normalize [info script]]]
+
+prep -design $script_dir -tag user_project_wrapper -overwrite
+set save_path $script_dir/../..
+
+verilog_elaborate
+
+init_floorplan
+
+place_io_ol
+
+add_macro_placement mprj 200 320 N
+
+manual_macro_placement f
+
+exec -ignorestderr openroad -exit $script_dir/gen_pdn.tcl
+set_def $::env(pdn_tmp_file_tag).def
+
+global_routing_or
+add_route_obs
+detailed_routing
+
+write_powered_verilog -power vccd1 -ground vssd1
+set_netlist $::env(lvs_result_file_tag).powered.v
+
+run_magic
+run_magic_spice_export
+
+save_views -lef_path $::env(magic_result_file_tag).lef \
+ -def_path $::env(tritonRoute_result_file_tag).def \
+ -gds_path $::env(magic_result_file_tag).gds \
+ -mag_path $::env(magic_result_file_tag).mag \
+ -save_path $save_path \
+ -tag $::env(RUN_TAG)
+
+run_magic_drc
+
+run_lvs; # requires run_magic_spice_export
+
+run_antenna_check
diff --git a/openlane/user_project_wrapper/pdn.tcl b/openlane/user_project_wrapper/pdn.tcl
new file mode 100644
index 0000000..b7e5f09
--- /dev/null
+++ b/openlane/user_project_wrapper/pdn.tcl
@@ -0,0 +1,66 @@
+# Power nets
+set ::power_nets $::env(_VDD_NET_NAME)
+set ::ground_nets $::env(_GND_NET_NAME)
+
+pdngen::specify_grid stdcell {
+ name grid
+ core_ring {
+ met5 {width $::env(_WIDTH) spacing $::env(_SPACING) core_offset $::env(_H_OFFSET)}
+ met4 {width $::env(_WIDTH) spacing $::env(_SPACING) core_offset $::env(_V_OFFSET)}
+ }
+ rails {
+ }
+ straps {
+ met4 {width $::env(_WIDTH) pitch $::env(_V_PITCH) offset $::env(_V_PDN_OFFSET)}
+ met5 {width $::env(_WIDTH) pitch $::env(_H_PITCH) offset $::env(_H_PDN_OFFSET)}
+ }
+ connect {{met4 met5}}
+}
+
+pdngen::specify_grid macro {
+ instance "obs_core_obs"
+ power_pins $::env(_VDD_NET_NAME)
+ ground_pins $::env(_GND_NET_NAME)
+ blockages "li1 met1 met2 met3 met4 met5"
+ straps {
+ }
+ connect {}
+}
+
+if { $::env(CONNECT_GRIDS) } {
+ pdngen::specify_grid macro {
+ power_pins "VPWR"
+ ground_pins "VGND"
+ blockages "met4"
+ straps {
+ }
+ connect {{met4_PIN_ver met5}}
+ }
+} else {
+ pdngen::specify_grid macro {
+ power_pins "VPWR"
+ ground_pins "VGND"
+ blockages "met4"
+ straps {
+ }
+ connect {}
+ }
+}
+
+pdngen::specify_grid macro {
+ power_pins $::env(_VDD_NET_NAME)
+ ground_pins $::env(_GND_NET_NAME)
+ blockages "li1 met1 met2 met3 met4"
+ straps {
+ }
+ connect {}
+}
+
+set ::halo 0
+
+# POWER or GROUND #Std. cell rails starting with power or ground rails at the bottom of the core area
+set ::rails_start_with "POWER" ;
+
+# POWER or GROUND #Upper metal stripes starting with power or ground rails at the left/bottom of the core area
+set ::stripes_start_with "POWER" ;
+