Merge remote-tracking branch 'openram_local/array_gen' into merge/full-array-gen-into-dev

# Conflicts:
#	technology/sky130/custom/sky130_col_cap_array.py
This commit is contained in:
Jesse Cirimelli-Low
2026-04-30 12:43:19 -07:00
65 changed files with 3289 additions and 3923 deletions
+2
View File
@@ -66,6 +66,8 @@ class contact(hierarchy_design):
self.offset = vector(0, 0)
self.implant_type = implant_type
self.well_type = well_type
# Module does not have pins, but has empty pin list.
self.pins = []
self.create_layout()
def create_layout(self):
+8 -5
View File
@@ -164,7 +164,7 @@ class instance(geometry):
An instance of a module with a specified location, rotation,
spice pins, and spice nets
"""
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False):
"""Initializes an instance to represent a module"""
super().__init__()
debug.check(mirror not in ["R90", "R180", "R270"],
@@ -176,6 +176,8 @@ class instance(geometry):
self.rotate = rotate
self.offset = vector(offset).snap_to_grid()
self.mirror = mirror
self.is_bitcell = is_bitcell
# track if the instance's spice pin connections have been made
self.connected = False
@@ -183,10 +185,11 @@ class instance(geometry):
# change attributes in these spice objects
self.spice_pins = copy.deepcopy(self.mod.pins)
self.spice_nets = copy.deepcopy(self.mod.nets)
for pin in self.spice_pins.values():
pin.set_inst(self)
for net in self.spice_nets.values():
net.set_inst(self)
if "contact" not in mod.name:
for pin in self.spice_pins.values():
pin.set_inst(self)
for net in self.spice_nets.values():
net.set_inst(self)
if OPTS.netlist_only:
self.width = 0
+121 -100
View File
@@ -9,6 +9,7 @@ import sys
import os
import re
from math import sqrt
from copy import deepcopy
from openram import debug
from openram.gdsMill import gdsMill
from openram import tech
@@ -460,21 +461,32 @@ class layout():
for pin in pin_list:
pin.rect = [pin.ll() - offset, pin.ur() - offset]
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False):
""" Adds an instance of a mod to this module """
# Contacts are not really instances, so skip them
if "contact" not in mod.name:
# Check that the instance name is unique
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name))
self.mods.add(mod)
self.inst_names.add(name)
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate, is_bitcell))
debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons
# debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
return self.insts[-1]
def add_existing_inst(self, inst, name):
#new_inst = deepcopy(inst)
new_inst = self.add_inst(name, inst.mod, offset=inst.offset, mirror=inst.mirror, rotate=inst.rotate, is_bitcell=inst.is_bitcell)
#new_inst.mod = inst.mod
self.mods.add(new_inst.mod)
#if name:
# new_inst.name = name
self.inst_names.add(new_inst.name)
#self.insts.append(new_inst)
debug.info(3, "adding existing instance{}".format(self.insts[-1]))
return self.insts[-1]
def get_inst(self, name):
""" Retrieve an instance by name """
for inst in self.insts:
@@ -2149,6 +2161,13 @@ class layout():
# Add the gnd ring
self.add_ring([ll, ur])
def reset_coordinates(self):
ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts]))
self.translate_all(ll)
self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts])
self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts])
def add_ring(self, bbox=None, width_mult=8, offset=0):
"""
Add a ring around the bbox
@@ -2201,7 +2220,7 @@ class layout():
size=(supply_vias,
supply_vias))
def add_power_ring(self):
def add_power_ring(self, h_layer="m2", v_layer="m1", top=True, bottom=True, left=True, right=True):
"""
Create vdd and gnd power rings around an area of the bounding box
argument. Must have a supply_rail_width and supply_rail_pitch
@@ -2209,118 +2228,120 @@ class layout():
left/right/top/bottom vdd/gnd center offsets for use in other
modules..
"""
[ll, ur] = self.bbox
supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width
supply_rail_spacing = self.supply_rail_pitch
height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing
width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
# LEFT vertical rails
offset = ll + vector(-2 * self.supply_rail_pitch,
-2 * self.supply_rail_pitch)
left_gnd_pin = self.add_layout_pin(text="gnd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
if left:
offset = ll + vector(-2*self.supply_rail_pitch,
-2*self.supply_rail_pitch)
self.left_gnd_pin = self.add_layout_pin(text="gnd",
layer=v_layer,
offset=offset,
width=self.supply_rail_width,
height=height + 2 * supply_rail_spacing)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
left_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
self.left_vdd_pin = self.add_layout_pin(text="vdd",
layer=v_layer,
offset=offset,
width=self.supply_rail_width,
height=height)
# RIGHT vertical rails
offset = vector(ur.x, ll.y) + vector(0, -2 * self.supply_rail_pitch)
right_gnd_pin = self.add_layout_pin(text="gnd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
if right:
# RIGHT vertical railsteac a 460
offset = vector(ur.x, ll.y) + vector(2 * self.supply_rail_pitch - self.supply_rail_width,
-2 * self.supply_rail_pitch)
self.right_gnd_pin = self.add_layout_pin(text="gnd",
layer=v_layer,
offset=offset,
width=self.supply_rail_width,
height=height + 2* supply_rail_spacing)
offset = vector(ur.x, ll.y) + vector(self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
right_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
offset = vector(ur.x, ll.y) + vector(1 * self.supply_rail_pitch - self.supply_rail_width,
-1 * self.supply_rail_pitch)
self.right_vdd_pin = self.add_layout_pin(text="vdd",
layer=v_layer,
offset=offset,
width=self.supply_rail_width,
height=height)
# BOTTOM horizontal rails
offset = ll + vector(-2 * self.supply_rail_pitch,
-2 * self.supply_rail_pitch)
bottom_gnd_pin = self.add_layout_pin(text="gnd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
if bottom:
# BOTTOM horizontal rails
offset = ll + vector(-2 * self.supply_rail_pitch,
-2 * self.supply_rail_pitch)
self.bottom_gnd_pin = self.add_layout_pin(text="gnd",
layer=h_layer,
offset=offset,
width=width + 2 * supply_rail_spacing,
height=self.supply_rail_width)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
bottom_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
self.bottom_vdd_pin = self.add_layout_pin(text="vdd",
layer=h_layer,
offset=offset,
width=width,
height=self.supply_rail_width)
if top:
# TOP horizontal rails
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
2 * self.supply_rail_pitch - self.supply_rail_width)
self.top_gnd_pin = self.add_layout_pin(text="gnd",
layer=h_layer,
offset=offset,
width=width + 2 * supply_rail_spacing,
height=self.supply_rail_width)
# TOP horizontal rails
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
0)
top_gnd_pin = self.add_layout_pin(text="gnd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch,
self.supply_rail_pitch)
top_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch,
1 * self.supply_rail_pitch - self.supply_rail_width)
self.top_vdd_pin = self.add_layout_pin(text="vdd",
layer=h_layer,
offset=offset,
width=width,
height=self.supply_rail_width)
# Remember these for connecting things in the design
self.left_gnd_x_center = left_gnd_pin.cx()
self.left_vdd_x_center = left_vdd_pin.cx()
self.right_gnd_x_center = right_gnd_pin.cx()
self.right_vdd_x_center = right_vdd_pin.cx()
self.bottom_gnd_y_center = bottom_gnd_pin.cy()
self.bottom_vdd_y_center = bottom_vdd_pin.cy()
self.top_gnd_y_center = top_gnd_pin.cy()
self.top_vdd_y_center = top_vdd_pin.cy()
# Find the number of vias for this pitch
self.supply_vias = 1
while True:
c = factory.create(module_type="contact",
layer_stack=self.m1_stack,
dimensions=(self.supply_vias, self.supply_vias))
if c.second_layer_width < self.supply_rail_width and c.second_layer_height < self.supply_rail_width:
self.supply_vias += 1
else:
self.supply_vias -= 1
break
via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center),
vector(self.left_gnd_x_center, self.top_gnd_y_center),
vector(self.right_gnd_x_center, self.bottom_gnd_y_center),
vector(self.right_gnd_x_center, self.top_gnd_y_center),
vector(self.left_vdd_x_center, self.bottom_vdd_y_center),
vector(self.left_vdd_x_center, self.top_vdd_y_center),
vector(self.right_vdd_x_center, self.bottom_vdd_y_center),
vector(self.right_vdd_x_center, self.top_vdd_y_center)]
if left:
self.left_gnd_x_center = self.left_gnd_pin.cx()
self.left_vdd_x_center = self.left_vdd_pin.cx()
if right:
self.right_gnd_x_center = self.right_gnd_pin.cx()
self.right_vdd_x_center = self.right_vdd_pin.cx()
if bottom:
self.bottom_gnd_y_center = self.bottom_gnd_pin.cy()
self.bottom_vdd_y_center = self.bottom_vdd_pin.cy()
if top:
self.top_gnd_y_center = self.top_gnd_pin.cy()
self.top_vdd_y_center = self.top_vdd_pin.cy()
via_points = []
if left and bottom:
via_points.append((self.left_gnd_x_center, self.bottom_gnd_y_center))
if left and top:
via_points.append(vector(self.left_gnd_x_center, self.top_gnd_y_center))
if right and bottom:
via_points.append(vector(self.right_gnd_x_center, self.bottom_gnd_y_center))
if right and top:
via_points.append(vector(self.right_gnd_x_center, self.top_gnd_y_center))
if left and bottom:
via_points.append(vector(self.left_vdd_x_center, self.bottom_vdd_y_center))
if left and top:
via_points.append(vector(self.left_vdd_x_center, self.top_vdd_y_center))
if right and bottom:
via_points.append(vector(self.right_vdd_x_center, self.bottom_vdd_y_center))
if right and top:
via_points.append((self.right_vdd_x_center, self.top_vdd_y_center))
for pt in via_points:
self.add_via_center(layers=self.m1_stack,
offset=pt,
size=(self.supply_vias,
self.supply_vias))
self.add_via_stack_center(offset=pt,
from_layer=h_layer,
to_layer=v_layer,
min_area=True)
def pdf_write(self, pdf_name):
"""
Display the layout to a PDF file.
+3 -1
View File
@@ -50,6 +50,7 @@ class spice():
if not os.path.exists(self.lvs_file):
self.lvs_file = self.sp_file
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
# Holds subckts/mods for this module
self.mods = set()
# Holds the pins for this module (in order)
@@ -699,7 +700,8 @@ class spice():
def get_instance_connections(self):
conns = []
for inst in self.insts:
conns.append(inst.get_connections())
if "contact" not in inst.name:
conns.append(inst.get_connections())
return conns
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):