Source code for fabulous.fabric_generator.gds_generator.script.odb_power
"""OpenDB script to connect power rails for FABulous fabric."""
#
# Original src: https://github.com/mole99/librelane_plugin_fabulous/blob/main/librelane_plugin_fabulous/scripts/odb_power.py
# OpenDB script for custom Power for FABulous fabric
# This script places vertical PDN straps on top
# of already existing straps in order to tell OpenROAD
# that they should be considered connected and are pins
#
# Copyright (c) 2023 Sylvain Munaut <tnt@246tNt.com>
# Copyright (c) 2025 Leo Moser <leo.moser@pm.me>
# Copyright (c) 2026 FABulous Contributors
# SPDX-License-Identifier: Apache-2.0
#
from typing import Any
import click
import odb as design_odb
from librelane.logging.logger import info
from librelane.scripts.odbpy.reader import click_odb
@click.option(
"--power-names",
default=None,
type=str,
multiple=True,
help="The name(s) of the power port(s). Repeat the option for multiple ports.",
)
@click.option(
"--ground-names",
default=None,
type=str,
multiple=True,
help="The name(s) of the ground port(s). Repeat the option for multiple ports.",
)
@click.command()
@click_odb
[docs]
def power(
reader: Any, # noqa: ANN401
power_names: tuple[str],
ground_names: tuple[str],
) -> None:
"""Cycle through VDD_NETS and GND_NETS for the tiles using a custom script."""
info(f"propagated VDD_NETS are {power_names}")
info(f"propagated GND_NETS are {ground_names}")
# todo: run on multi-power test case
# odb argument here enables pytest with monkeypatch
for power_name in power_names:
propagate_supply_net(design_odb, reader, power_name, "POWER")
for ground_name in ground_names:
propagate_supply_net(design_odb, reader, ground_name, "GROUND")
[docs]
def propagate_supply_net(
layoutDb: Any, # noqa: ANN401
reader: Any, # noqa: ANN401
supply_name: str,
supply_type: str,
) -> None:
"""Connect single power rail for the tiles using a custom script."""
# Create nets, if they don't exist yet
net = reader.block.findNet(supply_name)
if net is None:
# Create net
net = layoutDb.dbNet.create(reader.block, supply_name)
net.setSpecial()
net.setSigType(supply_type)
info(f"Created {net.getName()} with type {net.getSigType()}")
supply_net = reader.block.findNet(supply_name)
# Create wires
supply_wire = layoutDb.dbSWire.create(supply_net, "ROUTED")
# Create bterms (top-level)
supply_bterm = layoutDb.dbBTerm.create(supply_net, supply_name)
supply_bterm.setIoType("INOUT")
supply_bterm.setSigType(supply_net.getSigType())
supply_bterm.setSpecial()
supply_bpin = layoutDb.dbBPin_create(supply_bterm)
# Connect instance-iterms to power nets,
# draw the wires and pins
for blk_inst in reader.block.getInsts():
info(f"Instance: {blk_inst.getName()}")
for iterm in blk_inst.getITerms():
iterm_name = iterm.getMTerm().getName()
iterm_sigtype = iterm.getMTerm().getSigType()
if iterm_name == supply_name:
info(f"Connecting {iterm_name} of type {iterm_sigtype}")
iterm.connect(supply_net)
inst_master = blk_inst.getMaster()
# Now, for each power/ground mterm
# Copy the geomtry of the pins to wires and top-level pins
for master_mterm in inst_master.getMTerms():
if master_mterm.getName() == supply_name:
for mterm_mpins in master_mterm.getMPins():
for mpins_dbox in mterm_mpins.getGeometry():
metal_layer = mpins_dbox.getTechLayer()
if master_mterm.getName() == supply_name:
layoutDb.dbSBox_create(
supply_wire,
metal_layer,
blk_inst.getLocation()[0] + mpins_dbox.xMin(),
blk_inst.getLocation()[1] + mpins_dbox.yMin(),
blk_inst.getLocation()[0] + mpins_dbox.xMax(),
blk_inst.getLocation()[1] + mpins_dbox.yMax(),
"STRIPE",
)
layoutDb.dbBox_create(
supply_bpin,
metal_layer,
blk_inst.getLocation()[0] + mpins_dbox.xMin(),
blk_inst.getLocation()[1] + mpins_dbox.yMin(),
blk_inst.getLocation()[0] + mpins_dbox.xMax(),
blk_inst.getLocation()[1] + mpins_dbox.yMax(),
)
supply_bpin.setPlacementStatus("FIRM")
if __name__ == "__main__":
power()