blob: 9688b1adf19a785f8b2764c5920e8fa594a2b928 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 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.
import os
import sys
import argparse
from subprocess import Popen, PIPE
import odb
parser = argparse.ArgumentParser(
description="Add cell power connections in the netlist. Useful for LVS purposes."
)
parser.add_argument("--input-def", "-d", required=True, help="DEF view of the design")
parser.add_argument(
"--input-lef",
"-l",
required=True,
help="LEF file needed to have a proper view of the design. Every cell having a pin labeled as a power pin (e.g., USE POWER) will be connected to the power/ground port of the design.",
)
parser.add_argument(
"--power-port",
"-v",
help="Name of the power port of the design. The power pin of the subcells will be connected to it by default",
)
parser.add_argument(
"--ground-port",
"-g",
help="Name of the ground port of the design. The ground pin of the subcells will be connected to it by default",
)
parser.add_argument(
"--powered-netlist",
"-pv",
help="A structural verilog netlist, readable by openroad, that includes extra power connections that are to be applied after connecting to the default power-port and ground-port specified.",
)
parser.add_argument("--ignore-missing-pins", "-q", action="store_true", required=False)
parser.add_argument(
"--output", "-o", default="output.def", help="Output modified netlist"
)
# parser.add_argument('--create-pg-ports',
# help='Create power and ground ports if not found')
args = parser.parse_args()
def_file_name = args.input_def
lef_file_name = args.input_lef
power_port_name = args.power_port
ground_port_name = args.ground_port
powered_netlist_file_name = args.powered_netlist
ignore_missing_pins = args.ignore_missing_pins
output_file_name = args.output
def get_power_ground_ports(ports):
vdd_ports = []
gnd_ports = []
for port in ports:
if port.getSigType() == "POWER" or port.getName() == power_port_name:
vdd_ports.append(port)
elif port.getSigType() == "GROUND" or port.getName() == ground_port_name:
gnd_ports.append(port)
return (vdd_ports, gnd_ports)
def find_power_ground_port(port_name, ports):
for port in ports:
if port.getName() == port_name:
return port
return None
db = odb.dbDatabase.create()
odb.read_lef(db, lef_file_name)
odb.read_def(db, def_file_name)
chip = db.getChip()
block = chip.getBlock()
design_name = block.getName()
print("Top-level design name:", design_name)
VDD_PORTS, GND_PORTS = get_power_ground_ports(block.getBTerms())
assert (
VDD_PORTS and GND_PORTS
), "No power ports found at the top-level. Make sure that they exist and have the USE POWER|GROUND property or they match the arguments specified with --power-port and --ground-port."
vdd_net_idx = None
for index, port in enumerate(VDD_PORTS):
if port.getNet().getName() == power_port_name:
vdd_net_idx = index
gnd_net_idx = None
for index, port in enumerate(GND_PORTS):
if port.getNet().getName() == ground_port_name:
gnd_net_idx = index
assert (
vdd_net_idx is not None
), "Can't find power net at the top-level. Make sure that argument specified with --power-port."
assert (
gnd_net_idx is not None
), "Can't find ground net at the top-level. Make sure that argument specified with --ground-port."
DEFAULT_VDD = VDD_PORTS[vdd_net_idx].getNet()
DEFAULT_GND = GND_PORTS[gnd_net_idx].getNet()
print("Default power net: ", DEFAULT_VDD.getName())
print("Default ground net:", DEFAULT_GND.getName())
print("Found a total of", len(VDD_PORTS), "power ports.")
print("Found a total of", len(GND_PORTS), "ground ports.")
modified_cells = 0
cells = block.getInsts()
for cell in cells:
iterms = cell.getITerms()
cell_name = cell.getName()
if len(iterms) == 0:
continue
VDD_ITERMS = []
GND_ITERMS = []
VDD_ITERM_BY_NAME = None
GND_ITERM_BY_NAME = None
for iterm in iterms:
if iterm.getSigType() == "POWER":
VDD_ITERMS.append(iterm)
elif iterm.getSigType() == "GROUND":
GND_ITERMS.append(iterm)
elif iterm.getMTerm().getName() == power_port_name:
VDD_ITERM_BY_NAME = iterm
elif iterm.getMTerm().getName() == ground_port_name: # note **PORT**
GND_ITERM_BY_NAME = iterm
if len(VDD_ITERMS) == 0:
print(
"Warning: No pins in the LEF view of", cell_name, " marked for use as power"
)
print(
"Warning: Attempting to match power pin by name (using top-level port name) for cell:",
cell_name,
)
if VDD_ITERM_BY_NAME is not None: # note **PORT**
print("Found", power_port_name, "using that as a power pin")
VDD_ITERMS.append(VDD_ITERM_BY_NAME)
if len(GND_ITERMS) == 0:
print(
"Warning: No pins in the LEF view of",
cell_name,
" marked for use as ground",
)
print(
"Warning: Attempting to match ground pin by name (using top-level port name) for cell:",
cell_name,
)
if GND_ITERM_BY_NAME is not None: # note **PORT**
print("Found", ground_port_name, "using that as a ground pin")
GND_ITERMS.append(GND_ITERM_BY_NAME)
if len(VDD_ITERMS) == 0 or len(GND_ITERMS) == 0:
print("Warning: not all power pins found for cell:", cell_name)
if ignore_missing_pins:
print("Warning: ignoring", cell_name, "!!!!!!!")
continue
else:
print("Exiting... Use --ignore-missing-pins to ignore such errors")
sys.exit(1)
if len(VDD_ITERMS) > 2:
print("Warning: cell", cell_name, "has", len(VDD_ITERMS), "power pins.")
if len(GND_ITERMS) > 2:
print("Warning: cell", cell_name, "has", len(GND_ITERMS), "ground pins.")
for VDD_ITERM in VDD_ITERMS:
if VDD_ITERM.isConnected():
pin_name = VDD_ITERM.getMTerm().getName()
cell_name = cell_name
print(
"Warning: power pin", pin_name, "of", cell_name, "is already connected"
)
print("Warning: ignoring", cell_name + "/" + pin_name, "!!!!!!!")
else:
VDD_ITERM.connect(DEFAULT_VDD)
for GND_ITERM in GND_ITERMS:
if GND_ITERM.isConnected():
pin_name = GND_ITERM.getMTerm().getName()
cell_name = cell_name
print(
"Warning: ground pin", pin_name, "of", cell_name, "is already connected"
)
print("Warning: ignoring", cell_name + "/" + pin_name, "!!!!!!!")
else:
GND_ITERM.connect(DEFAULT_GND)
modified_cells += 1
print(
"Modified power connections of",
modified_cells,
"cells (Remaining:",
len(cells) - modified_cells,
").",
)
# apply extra special connections taken from another netlist:
if powered_netlist_file_name is not None and os.path.exists(powered_netlist_file_name):
tmp_def_file = f"{os.path.splitext(powered_netlist_file_name)[0]}.def"
openroad_script = []
openroad_script.append(f"read_lef {lef_file_name}")
openroad_script.append(f"read_verilog {powered_netlist_file_name}")
openroad_script.append(f"link_design {design_name}")
openroad_script.append(f"write_def {tmp_def_file}")
openroad_script.append("exit")
p = Popen(["openroad"], stdout=PIPE, stdin=PIPE, stderr=PIPE, encoding="utf8")
openroad_script = "\n".join(openroad_script)
output = p.communicate(openroad_script)
print("STDOUT:")
print(output[0].strip())
print("STDERR:")
print(output[1].strip())
print("openroad exit code:", p.returncode)
assert p.returncode == 0, p.returncode
assert os.path.exists(tmp_def_file), "DEF file doesn't exist"
db_power = odb.dbDatabase.create()
odb.read_lef(db_power, lef_file_name)
odb.read_def(db_power, tmp_def_file)
chip_power = db_power.getChip()
block_power = chip_power.getBlock()
assert block_power.getName() == design_name
print("Successfully created a new database")
POWER_GROUND_PORT_NAMES = [port.getName() for port in VDD_PORTS + GND_PORTS]
# using get_power_ground_ports doesn't work since the pins weren't
# created using pdngen
power_ground_ports = [
port
for port in block_power.getBTerms()
if port.getName() in POWER_GROUND_PORT_NAMES
]
for port in power_ground_ports:
iterms = port.getNet().getITerms()
for iterm in iterms:
inst_name = iterm.getInst().getName()
pin_name = iterm.getMTerm().getName()
original_inst = block.findInst(inst_name)
assert original_inst is not None, (
"Instance "
+ inst_name
+ " not found in the original netlist. Perhaps it was optimized out during synthesis?"
)
original_iterm = original_inst.findITerm(pin_name)
assert original_iterm is not None, (
inst_name + " doesn't have a pin named " + pin_name
)
original_port = find_power_ground_port(
port.getName(), VDD_PORTS + GND_PORTS
)
assert original_port is not None, (
port.getName() + " not found in the original netlist."
)
original_iterm.connect(original_port.getNet())
print("Modified connections between", port.getName(), "and", inst_name)
odb.write_def(block, output_file_name)