CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-klayout

KLayout is a high performance layout viewer and editor that supports GDS and OASIS files and more formats

Pending
Overview
Eval results
Files

hierarchical-design.mddocs/

Hierarchical Design and Cell Management

Cell hierarchy management, instance creation and manipulation, library organization, and parametric cell (PCell) support for reusable design components in IC layout design.

Capabilities

Cell Instance Management

class CellInstArray:
    def __init__(self, cell_index: int, trans: Trans, na: int = 1, nb: int = 1, 
                 da: Vector = None, db: Vector = None):
        """
        Create a cell instance or instance array.
        
        Parameters:
        - cell_index: Index of the cell to instantiate
        - trans: Transformation for the instance
        - na: Number of instances in A direction (default 1)
        - nb: Number of instances in B direction (default 1)
        - da: Displacement vector for A direction array
        - db: Displacement vector for B direction array
        """
    
    @property
    def cell_index(self) -> int:
        """Get the instantiated cell index."""
    
    @property
    def trans(self) -> Trans:
        """Get the instance transformation."""
    
    @property
    def na(self) -> int:
        """Get number of instances in A direction."""
    
    @property
    def nb(self) -> int:
        """Get number of instances in B direction."""
    
    @property
    def da(self) -> Vector:
        """Get A direction displacement vector."""
    
    @property
    def db(self) -> Vector:
        """Get B direction displacement vector."""
    
    def is_regular_array(self) -> bool:
        """Check if this is a regular array (na > 1 or nb > 1)."""
    
    def bbox(self, layout: Layout) -> Box:
        """Get bounding box of the instance array."""
    
    def transform_into(self, trans: Trans) -> CellInstArray:
        """Apply additional transformation to instance."""

class DCellInstArray:
    def __init__(self, cell_index: int, trans: DCplxTrans):
        """Create a double precision cell instance."""
    
    @property
    def cell_index(self) -> int:
        """Get the instantiated cell index."""
    
    @property
    def trans(self) -> DCplxTrans:
        """Get the instance transformation."""

Cell Hierarchy Navigation

class Cell:
    def each_inst(self):
        """Iterate over all instances in this cell."""
    
    def each_parent_inst(self):
        """Iterate over all instances of this cell (parent instances)."""
    
    def each_child_cell(self):
        """Iterate over all child cells (cells instantiated by this cell)."""
    
    def each_parent_cell(self):
        """Iterate over all parent cells (cells that instantiate this cell)."""
    
    def child_cells(self) -> int:
        """Get number of child cells."""
    
    def parent_cells(self) -> int:
        """Get number of parent cells."""
    
    def hierarchy_levels(self) -> int:
        """Get maximum hierarchy depth below this cell."""
    
    def is_top(self) -> bool:
        """Check if this is a top-level cell (no parents)."""
    
    def is_leaf(self) -> bool:
        """Check if this is a leaf cell (no children)."""
    
    def is_proxy(self) -> bool:
        """Check if this is a proxy cell (library reference)."""

class Layout:
    def top_cells(self):
        """Get all top-level cells in the layout."""
    
    def top_cell(self) -> Cell:
        """Get the single top cell (if there is exactly one)."""
    
    def each_cell(self):
        """Iterate over all cells in the layout."""
    
    def cells(self) -> int:
        """Get total number of cells."""

Parametric Cells (PCells)

class PCellDeclaration:
    def __init__(self):
        """Base class for parametric cell declarations."""
    
    def display_name(self, parameters) -> str:
        """
        Get display name for the PCell with given parameters.
        
        Parameters:
        - parameters: Dictionary of parameter values
        
        Returns:
        str: Display name for this parameter set
        """
    
    def produce(self, layout: Layout, layers, parameters, cell: Cell) -> None:
        """
        Produce the PCell geometry.
        
        Parameters:
        - layout: Target layout
        - layers: Layer mapping
        - parameters: Parameter values
        - cell: Target cell to populate
        """
    
    def get_parameters(self) -> list:
        """Get list of parameter declarations."""
    
    def get_layers(self) -> list:
        """Get list of layer declarations."""

class PCellDeclarationHelper:
    def __init__(self, name: str):
        """
        Helper class for creating parametric cells.
        
        Parameters:
        - name: Name of the PCell
        """
    
    def param(self, name: str, param_type: type, description: str, default=None) -> None:
        """
        Declare a parameter.
        
        Parameters:
        - name: Parameter name
        - param_type: Parameter type (int, float, str, bool)
        - description: Parameter description
        - default: Default value
        """
    
    def layer(self, name: str, description: str, default: LayerInfo = None) -> None:
        """
        Declare a layer parameter.
        
        Parameters:
        - name: Layer parameter name
        - description: Layer description
        - default: Default layer info
        """
    
    def produce_impl(self) -> None:
        """
        Implementation method to be overridden.
        This method should create the actual geometry.
        """

class PCellParameterDeclaration:
    def __init__(self, name: str, param_type: type, description: str = "", 
                 default=None, choices=None):
        """
        Parameter declaration for PCells.
        
        Parameters:
        - name: Parameter name
        - param_type: Parameter type
        - description: Parameter description
        - default: Default value
        - choices: List of valid choices (for enum parameters)
        """
    
    @property
    def name(self) -> str:
        """Get parameter name."""
    
    @property
    def type(self) -> type:
        """Get parameter type."""
    
    @property
    def description(self) -> str:
        """Get parameter description."""

# Parameter types
class PCellParameterType:
    TypeInt = int
    TypeDouble = float
    TypeString = str
    TypeBoolean = bool
    TypeLayer = LayerInfo
    TypeShape = object

Library Management

class Library:
    def __init__(self, name: str = ""):
        """
        Create a library.
        
        Parameters:
        - name: Library name
        """
    
    @property
    def name(self) -> str:
        """Get library name."""
    
    def set_name(self, name: str) -> None:
        """Set library name."""
    
    def register_pcell(self, pcell_class, name: str) -> int:
        """
        Register a PCell class in the library.
        
        Parameters:
        - pcell_class: PCell declaration class
        - name: PCell name in library
        
        Returns:
        int: PCell ID
        """
    
    def layout(self) -> Layout:
        """Get the library layout."""
    
    def delete(self) -> None:
        """Delete the library."""

class Technology:
    def __init__(self, name: str = ""):
        """
        Create a technology definition.
        
        Parameters:
        - name: Technology name
        """
    
    @property
    def name(self) -> str:
        """Get technology name."""
    
    def load(self, tech_file: str) -> None:
        """Load technology from file."""
    
    def save(self, tech_file: str) -> None:
        """Save technology to file."""

Usage Examples

Creating Cell Hierarchies

import klayout.db as db

# Create layout with hierarchy
layout = db.Layout()

# Create leaf cell (basic building block)
nmos_cell = layout.create_cell("NMOS_TRANSISTOR")
layer_active = layout.layer(db.LayerInfo(1, 0))
layer_poly = layout.layer(db.LayerInfo(2, 0))
layer_contact = layout.layer(db.LayerInfo(3, 0))

# Add transistor geometry
nmos_cell.shapes(layer_active).insert(db.Box(0, 0, 200, 100))
nmos_cell.shapes(layer_poly).insert(db.Box(50, -20, 150, 120))
nmos_cell.shapes(layer_contact).insert(db.Box(25, 25, 75, 75))
nmos_cell.shapes(layer_contact).insert(db.Box(125, 25, 175, 75))

# Create intermediate cell (logic gate)
inverter_cell = layout.create_cell("INVERTER")

# Instantiate NMOS in inverter
nmos_instance = db.CellInstArray(nmos_cell.cell_index, db.Trans(db.Point(0, 0)))
inverter_cell.insert(nmos_instance)

# Add PMOS (simplified - just another NMOS for this example)
pmos_instance = db.CellInstArray(nmos_cell.cell_index, 
                                db.Trans(0, True, db.Point(0, 200)))  # Mirrored
inverter_cell.insert(pmos_instance)

# Create top-level cell
top_cell = layout.create_cell("LOGIC_BLOCK")

# Create array of inverters
for x in range(0, 1000, 250):
    for y in range(0, 500, 300):
        inv_instance = db.CellInstArray(inverter_cell.cell_index, 
                                      db.Trans(db.Point(x, y)))
        top_cell.insert(inv_instance)

layout.write("hierarchy_example.gds")

Instance Arrays

import klayout.db as db

layout = db.Layout()
unit_cell = layout.create_cell("UNIT")
top_cell = layout.create_cell("TOP")

# Add content to unit cell
layer = layout.layer(db.LayerInfo(1, 0))
unit_cell.shapes(layer).insert(db.Box(0, 0, 10, 10))

# Create 1D array
array_1d = db.CellInstArray(
    unit_cell.cell_index,
    db.Trans(db.Point(0, 0)),     # Base transformation
    na=10,                        # 10 instances in A direction
    nb=1,                         # 1 instance in B direction
    da=db.Vector(15, 0),         # 15 unit spacing in X
    db=db.Vector(0, 0)           # No B direction spacing
)
top_cell.insert(array_1d)

# Create 2D array
array_2d = db.CellInstArray(
    unit_cell.cell_index,
    db.Trans(db.Point(0, 50)),    # Offset from 1D array
    na=8,                         # 8 instances in A direction
    nb=6,                         # 6 instances in B direction
    da=db.Vector(15, 0),         # 15 unit spacing in X
    db=db.Vector(0, 20)          # 20 unit spacing in Y
)
top_cell.insert(array_2d)

print(f"1D Array bbox: {array_1d.bbox(layout)}")
print(f"2D Array bbox: {array_2d.bbox(layout)}")

layout.write("array_example.gds")

Hierarchy Navigation

import klayout.db as db

layout = db.Layout()
layout.read("complex_design.gds")

# Find all top cells
top_cells = list(layout.top_cells())
print(f"Found {len(top_cells)} top-level cells")

if top_cells:
    top_cell = top_cells[0]
    print(f"Analyzing cell: {top_cell.name}")
    
    # Analyze hierarchy
    print(f"Child cells: {top_cell.child_cells()}")
    print(f"Parent cells: {top_cell.parent_cells()}")
    print(f"Hierarchy levels: {top_cell.hierarchy_levels()}")
    
    # Walk through hierarchy
    def analyze_cell(cell, level=0):
        indent = "  " * level
        print(f"{indent}Cell: {cell.name}")
        print(f"{indent}  Instances: {sum(1 for _ in cell.each_inst())}")
        print(f"{indent}  Is leaf: {cell.is_leaf()}")
        
        # Analyze instances
        for inst in cell.each_inst():
            child_cell = layout.cell(inst.cell_index)
            print(f"{indent}  -> {child_cell.name} at {inst.trans.disp}")
            
            # Recurse for first few levels
            if level < 3:
                analyze_cell(child_cell, level + 1)
    
    analyze_cell(top_cell)

Simple Parametric Cell Example

import klayout.db as db

class ResistorPCell(db.PCellDeclarationHelper):
    def __init__(self):
        super().__init__("Resistor")
        
        # Declare parameters
        self.param("width", db.PCellParameterType.TypeDouble, "Width", 1.0)
        self.param("length", db.PCellParameterType.TypeDouble, "Length", 10.0)
        self.param("num_contacts", db.PCellParameterType.TypeInt, "Number of contacts", 2)
        
        # Declare layers
        self.layer("poly", "Poly layer", db.LayerInfo(1, 0))
        self.layer("contact", "Contact layer", db.LayerInfo(2, 0))
    
    def produce_impl(self):
        # Get parameter values
        width = self.width
        length = self.length
        num_contacts = self.num_contacts
        
        # Get layer indices
        poly_layer = self.layout.layer(self.poly)
        contact_layer = self.layout.layer(self.contact)
        
        # Create resistor body
        resistor_body = db.Box(0, 0, int(length * 1000), int(width * 1000))
        self.cell.shapes(poly_layer).insert(resistor_body)
        
        # Add contacts
        contact_size = min(int(width * 200), int(length * 200 / num_contacts))
        for i in range(num_contacts):
            if num_contacts == 1:
                x = int(length * 500)  # Center
            else:
                x = int(length * 1000 * i / (num_contacts - 1))
            
            y = int(width * 500 - contact_size / 2)
            contact = db.Box(x - contact_size//2, y, 
                           x + contact_size//2, y + contact_size)
            self.cell.shapes(contact_layer).insert(contact)

# Register and use the PCell
library = db.Library("MyLibrary")
resistor_id = library.register_pcell(ResistorPCell(), "Resistor")

# Create layout using the PCell
layout = db.Layout()
top_cell = layout.create_cell("TOP")

# Instantiate PCell with different parameters
params1 = {"width": 2.0, "length": 20.0, "num_contacts": 3}
params2 = {"width": 1.5, "length": 15.0, "num_contacts": 2}

# Note: Actual PCell instantiation requires library integration
# This is a simplified example

Cell Manipulation and Analysis

import klayout.db as db

layout = db.Layout()
layout.read("design.gds")

def analyze_hierarchy(layout):
    """Analyze layout hierarchy and provide statistics."""
    
    stats = {
        "total_cells": layout.cells(),
        "top_cells": len(list(layout.top_cells())),
        "leaf_cells": 0,
        "max_depth": 0,
        "total_instances": 0
    }
    
    # Analyze each cell
    for cell in layout.each_cell():
        if cell.is_leaf():
            stats["leaf_cells"] += 1
        
        # Count instances
        instance_count = sum(1 for _ in cell.each_inst())
        stats["total_instances"] += instance_count
        
        # Track maximum depth
        depth = cell.hierarchy_levels()
        if depth > stats["max_depth"]:
            stats["max_depth"] = depth
    
    return stats

def find_unused_cells(layout):
    """Find cells that are never instantiated."""
    
    used_cells = set()
    
    # Mark all instantiated cells as used
    for cell in layout.each_cell():
        for inst in cell.each_inst():
            used_cells.add(inst.cell_index)
    
    # Find unused cells
    unused_cells = []
    for cell in layout.each_cell():
        if cell.cell_index not in used_cells and not cell.is_top():
            unused_cells.append(cell)
    
    return unused_cells

def flatten_hierarchy(cell, target_cell, level=1):
    """Flatten cell hierarchy to specified level."""
    
    if level <= 0:
        return
    
    instances_to_remove = []
    
    for inst in cell.each_inst():
        child_cell = cell.layout().cell(inst.cell_index)
        
        # Copy child cell content to target cell with transformation
        for layer in range(cell.layout().layers()):
            for shape in child_cell.shapes(layer).each():
                transformed_shape = shape.transformed(inst.trans)
                target_cell.shapes(layer).insert(transformed_shape)
        
        # Recursively flatten child cells
        flatten_hierarchy(child_cell, target_cell, level - 1)
        
        instances_to_remove.append(inst)
    
    # Remove flattened instances
    for inst in instances_to_remove:
        cell.erase(inst)

# Use the analysis functions
stats = analyze_hierarchy(layout)
print(f"Hierarchy Statistics: {stats}")

unused = find_unused_cells(layout)
print(f"Unused cells: {[cell.name for cell in unused]}")

# Flatten top cell by 2 levels
if layout.top_cell():
    flattened_cell = layout.create_cell("FLATTENED")
    flatten_hierarchy(layout.top_cell(), flattened_cell, 2)

Advanced Instance Transformations

import klayout.db as db

layout = db.Layout()
base_cell = layout.create_cell("BASE")
pattern_cell = layout.create_cell("PATTERN")

# Add content to base cell
layer = layout.layer(db.LayerInfo(1, 0))
base_cell.shapes(layer).insert(db.Box(0, 0, 100, 50))

# Create various transformed instances
transformations = [
    db.Trans(0, False, db.Point(0, 0)),       # Original
    db.Trans(1, False, db.Point(200, 0)),     # 90° rotation
    db.Trans(2, False, db.Point(200, 200)),   # 180° rotation
    db.Trans(3, False, db.Point(0, 200)),     # 270° rotation
    db.Trans(0, True, db.Point(400, 0)),      # X mirror
    db.Trans(1, True, db.Point(600, 0)),      # X mirror + 90° rotation
]

for i, trans in enumerate(transformations):
    instance = db.CellInstArray(base_cell.cell_index, trans)
    
    # Apply additional transformation
    if i > 2:  # Scale some instances
        scaled_trans = db.Trans(db.Vector(50, 25))  # Additional offset
        modified_instance = instance.transform_into(scaled_trans)
        pattern_cell.insert(modified_instance)
    else:
        pattern_cell.insert(instance)

# Create complex patterns with arrays and transformations
spiral_cell = layout.create_cell("SPIRAL")
radius = 50
angle_step = 30

for i in range(12):  # 12 positions around circle
    angle_rad = i * angle_step * 3.14159 / 180
    x = int(radius * cos(angle_rad))
    y = int(radius * sin(angle_rad))
    
    # Rotation to face outward
    rotation = i * angle_step / 90  # Convert to 90-degree increments
    
    trans = db.Trans(int(rotation) % 4, False, db.Point(x, y))
    instance = db.CellInstArray(base_cell.cell_index, trans)
    spiral_cell.insert(instance)

layout.write("advanced_instances.gds")

Install with Tessl CLI

npx tessl i tessl/pypi-klayout

docs

database-operations.md

file-io.md

hierarchical-design.md

index.md

layout-viewer.md

shape-operations.md

transformations.md

verification.md

tile.json