unihd-cag / skillbridge

A seamless python to Cadence Virtuoso Skill interface
https://unihd-cag.github.io/skillbridge/
GNU Lesser General Public License v3.0
181 stars 38 forks source link

Selecting pin labels #267

Open dvancil opened 6 days ago

dvancil commented 6 days ago

I have a standard Skill routine that selects pins and snaps them to their XL compatible locations and the labels automatically move with them. The Skillbridge equivalent I've put together so far fails to move the labels with the pins. Is there something (an attribute, etc) I need to search for to select and move labels?

nielsbuwen commented 6 days ago

Hi,

can you share the exact code that is making problems? Both SKILL and Python. I can then take a look if something went wrong while translating.

dvancil commented 6 days ago

procedure(DVMovePins() cv=geGetWindowCellView() foreach(term cv~>terminals ; iterate over all the pins for this terminal foreach(pin term~>pins pinFig=pin~>fig net=term~>net ; only do this if there aren't more than one inst term unless(cdr(net~>instTerms) instTerm=car(net~>instTerms) childTerm=instTerm~>term childPinFig=car(instTerm~>term~>pins)~>fig ; location within the instance master location=centerBox(childPinFig~>bBox) ; transform to the top level coordinate system location=dbTransformPoint(location instTerm~>inst~>transform) pinFigLocation=centerBox(pinFig~>bBox) ; calculate the overall transform to move the pin to the new location ;transform=dbConcatTransform(list(-xCoord(pinFigLocation):-yCoord(pinFigLocation) "R0" 1) location) transform=dbConcatTransform(list(-xCoord(pinFigLocation):-yCoord(pinFigLocation) "R0" 1) list(xCoord(location):yCoord(location) "R0" 1)) dbMoveFig(pinFig cv transform) ) ) ) )

Python (seems terribly long by comparison):#this code moves the pin shapes but not the labels

import sys from skillbridge import Workspace

class Pin: def init(self, net_name, bBox, label=None): self.net = Net(net_name) self.bBox = bBox self.label = label # Associate label with pin

class Label: def init(self, text, location): self.text = text self.location = location

class Net: def init(self, name): self.name = name

class Instance: def init(self, pin): self.pin = pin

class Shape: def init(self, pin): self.pin = pin

class Terminal: def init(self, name, pins): self.name = name self.pins = pins

class CellView: def init(self, libName, cellName, viewName, instances, shapes, terminals, labels): self.libName = libName self.cellName = cellName self.viewName = viewName self.instances = instances self.shapes = shapes self.terminals = terminals self.labels = labels

def get_current_cell_view(): try: ws = Workspace.open() # Initialize the workspace print("Workspace opened successfully.")

    cv = ws.ge.get_edit_cell_view()  # Get the currently open cell view
    if cv:
        print(f"Cell view opened: {cv.libName}, {cv.cellName}, {cv.viewName}")
        instances = cv.instances if cv.instances is not None else []  # Ensure instances is not None
        shapes = cv.shapes if cv.shapes is not None else []  # Ensure shapes is not None
        terminals = cv.terminals if cv.terminals is not None else []  # Ensure terminals is not None
        labels = cv.labels if cv.labels is not None else []  # Ensure labels is not None
        return CellView(cv.libName, cv.cellName, cv.viewName, instances, shapes, terminals, labels)
    print("Failed to open cell view.")
except Exception as e:
    print(f"An error occurred: {e}")
return None

def centerBox(bBox): """Calculate the center of a bounding box.""" center_x = (bBox[0][0] + bBox[1][0]) / 2 center_y = (bBox[0][1] + bBox[1][1]) / 2 return [center_x, center_y]

def dbTransformPoint(point, transform): """Transform a point using the given transformation.""" x, y = point dx, dy = transform # Extract the first two elements directly return [x + dx, y + dy]

def dbConcatTransform(transform1, transform2): """Concatenate two transformations.""" x1, y1 = transform1 x2, y2 = transform2 return [x1 + x2, y1 + y2]

def dbMoveFig(pinFig, cv, transform): """Move the pin figure to the new location.""" new_location = dbTransformPoint(centerBox(pinFig.bBox), transform) pinFig.bBox = [[new_location[0] - 0.1, new_location[1] - 0.1], [new_location[0] + 0.1, new_location[1] + 0.1]] print(f"Moved pin to new location: {new_location}")

def dbMoveLabel(label, transform): """Move the label to the new location.""" new_location = dbTransformPoint(label.location, transform) label.location = new_location print(f"Moved label to new location: {new_location}")

def DVgetPinInfo(file=None): cv = get_current_cell_view() if cv is not None:

Retrieve pins from instances and shapes

    inst_pins = [x.pin for x in cv.instances if hasattr(x, 'pin') and x.pin is not None]
    shape_pins = [x.pin for x in cv.shapes if hasattr(x, 'pin') and x.pin is not None]

    if inst_pins or shape_pins:
        all_pins = inst_pins + shape_pins
        file_ptr = None

        if isinstance(file, str):
            file_ptr = open(file, 'w')
        elif file is not None:
            file_ptr = file
        else:
            file_ptr = sys.stdout

        output_str = f"{cv.libName} {cv.cellName} {cv.viewName} contains the following pins:\n"

        for pin in all_pins:
            if pin and pin.net:  # Check if pin is not None and has a net attribute
                bBox = getattr(pin.fig, 'b_box', None) if hasattr(pin, 'fig') else None
                layer = getattr(pin.fig, 'layer_name', 'Unknown') if hasattr(pin, 'fig') else 'Unknown'
                purpose = getattr(pin.fig, 'purpose', 'Unknown') if hasattr(pin, 'fig') else 'Unknown'
                if bBox:
                    x_dim = abs(bBox[1][0] - bBox[0][0])
                    y_dim = abs(bBox[1][1] - bBox[0][1])
                    center_x = (bBox[0][0] + bBox[1][0]) / 2
                    center_y = (bBox[0][1] + bBox[1][1]) / 2
                    output_str += f"Name: {pin.net.name:<5} bBox: {bBox} x_dim: {x_dim:.2f} y_dim: {y_dim:.2f} center: ({center_x:.2f}, {center_y:.2f}) layer: {layer} purpose: {purpose}\n"
                else:
                    output_str += f"Name: {pin.net.name:<5} bBox: None layer: {layer} purpose: {purpose}\n"

        print(output_str.strip(), file=file_ptr)
        print(output_str.strip())

        if file_ptr not in (sys.stdout, None):
            file_ptr.close()

def DVMovePins(): cv = get_current_cell_view() if cv is not None: for term in cv.terminals: for pin in term.pins: pinFig = pin.fig net = term.net if len(net.instTerms) == 1: instTerm = net.instTerms[0] childTerm = instTerm.term childPinFig = childTerm.pins[0].fig location = centerBox(childPinFig.bBox) print(f"Original location: {location}") print(f"Transform: {instTerm.inst.transform}") location = dbTransformPoint(location, instTerm.inst.transform[0]) # Extract the first element which is the list print(f"Transformed location: {location}") pinFigLocation = centerBox(pinFig.bBox) transform = dbConcatTransform([-pinFigLocation[0], -pinFigLocation[1]], [location[0], location[1]]) print(f"Pin original location: {pinFigLocation}") print(f"Transform to apply: {transform}") dbMoveFig(pinFig, cv, transform) print(f"Pin new location: {centerBox(pinFig.bBox)}")

Move associated label

                if pin.label:
                    dbMoveLabel(pin.label, transform)

Example usage:

DVgetPinInfo('output.txt') DVMovePins()

nielsbuwen commented 5 days ago

That's a lot of code. Can you try to isolate the parts that reproduce the problem. Otherwise it will take quite some time for me to go through all of that. I will do it when i find enough time, but a minimal example would really help me.

dvancil commented 4 days ago

Yes, I apologize for that. An attempted "direct" translation of the Skill code to Skillbridge failed to move the label with its associated pin (pins move just fine, but not labels). I continued going deeper trying to select and move the labels with the pins. The standard Skill routine moves the labels with the pins automatically (as does any manual movement of pins in the Virtuoso tool--actually, you have to go out of your way to disassociate the label from its pin). I will try to recover (or recreate) the first and much simpler version of Skillbridge I had for this function. I would be nice to have this working in Skillbridge, but I also wanted to see if you know a way to run a regular Skill routine from Python/Skillbridge (load and execute in Cadence from Python). I have ways of doing it through a shell script, but would like to roll it into my Python code. Thanks you for your help.

dvancil commented 4 days ago

OK, here is the starting (and much less convoluted) version. This correctly moves all pins to their XL compatible locations, but fails to move the labels.

import sys from skillbridge import Workspace

class Pin: def init(self, net_name, bBox): self.net = Net(net_name) self.bBox = bBox

class Net: def init(self, name): self.name = name

class Terminal: def init(self, name, pins): self.name = name self.pins = pins

class CellView: def init(self, libName, cellName, viewName, terminals): self.libName = libName self.cellName = cellName self.viewName = viewName self.terminals = terminals

def get_current_cell_view(): try: ws = Workspace.open() # Initialize the workspace print("Workspace opened successfully.") cv = ws.ge.get_edit_cell_view() # Get the currently open cell view if cv: print(f"Cell view opened: {cv.libName}, {cv.cellName}, {cv.viewName}") terminals = cv.terminals if cv.terminals is not None else [] # Ensure terminals is not None return CellView(cv.libName, cv.cellName, cv.viewName, terminals) print("Failed to open cell view.") except Exception as e: print(f"An error occurred: {e}") return None

def centerBox(bBox): """Calculate the center of a bounding box.""" center_x = round((bBox[0][0] + bBox[1][0]) / 2, 2) center_y = round((bBox[0][1] + bBox[1][1]) / 2, 2) return [center_x, center_y]

def dbTransformPoint(point, transform): """Transform a point using the given transformation.""" x, y = point dx, dy = transform # Extract the first two elements directly return [round(x + dx, 2), round(y + dy, 2)]

def dbConcatTransform(transform1, transform2): """Concatenate two transformations.""" x1, y1 = transform1 x2, y2 = transform2 return [round(x1 + x2, 2), round(y1 + y2, 2)]

def dbMoveFig(pinFig, cv, transform): """Move the pin figure to the new location.""" new_location = dbTransformPoint(centerBox(pinFig.bBox), transform) pinFig.bBox = [[new_location[0] - 0.1, new_location[1] - 0.1], [new_location[0] + 0.1, new_location[1] + 0.1]]

def DVMovePins(): cv = get_current_cell_view() if cv is not None: for term in cv.terminals: for pin in term.pins: pinFig = pin.fig net = term.net if len(net.instTerms) == 1: instTerm = net.instTerms[0] childTerm = instTerm.term childPinFig = childTerm.pins[0].fig location = centerBox(childPinFig.bBox) location = dbTransformPoint(location, instTerm.inst.transform[0]) # Extract the first element which is the list pinFigLocation = centerBox(pinFig.bBox) transform = dbConcatTransform([-pinFigLocation[0], -pinFigLocation[1]], [location[0], location[1]]) dbMoveFig(pinFig, cv, transform) else: print("No cell view found.")

DVMovePins()