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" ; +