Merge branch 'singleport_refactor' into array_gen

This commit is contained in:
Jesse Cirimelli-Low
2025-02-24 23:26:28 -08:00
35 changed files with 1133 additions and 2828 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git
# Use this for development # Use this for development
#SRAM_LIB_GIT_REPO ?= [email protected]:VLSIDA/sky130_fd_bd_sram.git #SRAM_LIB_GIT_REPO ?= [email protected]:VLSIDA/sky130_fd_bd_sram.git
#SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git #SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git
SRAM_LIB_GIT_COMMIT ?= dd64256961317205343a3fd446908b42bafba388 SRAM_LIB_GIT_COMMIT ?= 8dccd8d8ddb0a9de1b02207b2cd0c0d697807aa9
SKY130_PDK ?= $(PDK_ROOT)/sky130A SKY130_PDK ?= $(PDK_ROOT)/sky130A
GF180_PDK ?= $(PDK_ROOT)/gf180mcuD GF180_PDK ?= $(PDK_ROOT)/gf180mcuD
+2
View File
@@ -66,6 +66,8 @@ class contact(hierarchy_design):
self.offset = vector(0, 0) self.offset = vector(0, 0)
self.implant_type = implant_type self.implant_type = implant_type
self.well_type = well_type self.well_type = well_type
# Module does not have pins, but has empty pin list.
self.pins = []
self.create_layout() self.create_layout()
def create_layout(self): 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, An instance of a module with a specified location, rotation,
spice pins, and spice nets 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""" """Initializes an instance to represent a module"""
super().__init__() super().__init__()
debug.check(mirror not in ["R90", "R180", "R270"], debug.check(mirror not in ["R90", "R180", "R270"],
@@ -176,6 +176,8 @@ class instance(geometry):
self.rotate = rotate self.rotate = rotate
self.offset = vector(offset).snap_to_grid() self.offset = vector(offset).snap_to_grid()
self.mirror = mirror self.mirror = mirror
self.is_bitcell = is_bitcell
# track if the instance's spice pin connections have been made # track if the instance's spice pin connections have been made
self.connected = False self.connected = False
@@ -183,10 +185,11 @@ class instance(geometry):
# change attributes in these spice objects # change attributes in these spice objects
self.spice_pins = copy.deepcopy(self.mod.pins) self.spice_pins = copy.deepcopy(self.mod.pins)
self.spice_nets = copy.deepcopy(self.mod.nets) self.spice_nets = copy.deepcopy(self.mod.nets)
for pin in self.spice_pins.values(): if "contact" not in mod.name:
pin.set_inst(self) for pin in self.spice_pins.values():
for net in self.spice_nets.values(): pin.set_inst(self)
net.set_inst(self) for net in self.spice_nets.values():
net.set_inst(self)
if OPTS.netlist_only: if OPTS.netlist_only:
self.width = 0 self.width = 0
+121 -100
View File
@@ -9,6 +9,7 @@ import sys
import os import os
import re import re
from math import sqrt from math import sqrt
from copy import deepcopy
from openram import debug from openram import debug
from openram.gdsMill import gdsMill from openram.gdsMill import gdsMill
from openram import tech from openram import tech
@@ -460,21 +461,32 @@ class layout():
for pin in pin_list: for pin in pin_list:
pin.rect = [pin.ll() - offset, pin.ur() - offset] 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 """ """ Adds an instance of a mod to this module """
# Contacts are not really instances, so skip them # Contacts are not really instances, so skip them
if "contact" not in mod.name: if "contact" not in mod.name:
# Check that the instance name is unique # 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)) debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name))
self.mods.add(mod) self.mods.add(mod)
self.inst_names.add(name) 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])) debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons # This is commented out for runtime reasons
# debug.info(4, "instance list: " + ",".join(x.name for x in self.insts)) # debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
return self.insts[-1] 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): def get_inst(self, name):
""" Retrieve an instance by name """ """ Retrieve an instance by name """
for inst in self.insts: for inst in self.insts:
@@ -2149,6 +2161,13 @@ class layout():
# Add the gnd ring # Add the gnd ring
self.add_ring([ll, ur]) 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): def add_ring(self, bbox=None, width_mult=8, offset=0):
""" """
Add a ring around the bbox Add a ring around the bbox
@@ -2201,7 +2220,7 @@ class layout():
size=(supply_vias, size=(supply_vias,
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 Create vdd and gnd power rings around an area of the bounding box
argument. Must have a supply_rail_width and supply_rail_pitch 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 left/right/top/bottom vdd/gnd center offsets for use in other
modules.. modules..
""" """
[ll, ur] = self.bbox [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 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 width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
# LEFT vertical rails # LEFT vertical rails
offset = ll + vector(-2 * self.supply_rail_pitch, if left:
-2 * self.supply_rail_pitch) offset = ll + vector(-2*self.supply_rail_pitch,
left_gnd_pin = self.add_layout_pin(text="gnd", -2*self.supply_rail_pitch)
layer="m2", self.left_gnd_pin = self.add_layout_pin(text="gnd",
offset=offset, layer=v_layer,
width=self.supply_rail_width, offset=offset,
height=height) width=self.supply_rail_width,
height=height + 2 * supply_rail_spacing)
offset = ll + vector(-1 * self.supply_rail_pitch, offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch) -1 * self.supply_rail_pitch)
left_vdd_pin = self.add_layout_pin(text="vdd", self.left_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2", layer=v_layer,
offset=offset, offset=offset,
width=self.supply_rail_width, width=self.supply_rail_width,
height=height) height=height)
# RIGHT vertical rails if right:
offset = vector(ur.x, ll.y) + vector(0, -2 * self.supply_rail_pitch) # RIGHT vertical railsteac a 460
right_gnd_pin = self.add_layout_pin(text="gnd", offset = vector(ur.x, ll.y) + vector(2 * self.supply_rail_pitch - self.supply_rail_width,
layer="m2", -2 * self.supply_rail_pitch)
offset=offset, self.right_gnd_pin = self.add_layout_pin(text="gnd",
width=self.supply_rail_width, layer=v_layer,
height=height) offset=offset,
width=self.supply_rail_width,
height=height + 2* supply_rail_spacing)
offset = vector(ur.x, ll.y) + vector(self.supply_rail_pitch, offset = vector(ur.x, ll.y) + vector(1 * self.supply_rail_pitch - self.supply_rail_width,
-1 * self.supply_rail_pitch) -1 * self.supply_rail_pitch)
right_vdd_pin = self.add_layout_pin(text="vdd", self.right_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2", layer=v_layer,
offset=offset, offset=offset,
width=self.supply_rail_width, width=self.supply_rail_width,
height=height) height=height)
# BOTTOM horizontal rails if bottom:
offset = ll + vector(-2 * self.supply_rail_pitch, # BOTTOM horizontal rails
-2 * self.supply_rail_pitch) offset = ll + vector(-2 * self.supply_rail_pitch,
bottom_gnd_pin = self.add_layout_pin(text="gnd", -2 * self.supply_rail_pitch)
layer="m1", self.bottom_gnd_pin = self.add_layout_pin(text="gnd",
offset=offset, layer=h_layer,
width=width, offset=offset,
height=self.supply_rail_width) width=width + 2 * supply_rail_spacing,
height=self.supply_rail_width)
offset = ll + vector(-1 * self.supply_rail_pitch, offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch) -1 * self.supply_rail_pitch)
bottom_vdd_pin = self.add_layout_pin(text="vdd", self.bottom_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1", layer=h_layer,
offset=offset, offset=offset,
width=width, width=width,
height=self.supply_rail_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(-1 * self.supply_rail_pitch,
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch, 1 * self.supply_rail_pitch - self.supply_rail_width)
0) self.top_vdd_pin = self.add_layout_pin(text="vdd",
top_gnd_pin = self.add_layout_pin(text="gnd", layer=h_layer,
layer="m1", offset=offset,
offset=offset, width=width,
width=width, height=self.supply_rail_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)
# Remember these for connecting things in the design # Remember these for connecting things in the design
self.left_gnd_x_center = left_gnd_pin.cx() if left:
self.left_vdd_x_center = left_vdd_pin.cx() self.left_gnd_x_center = self.left_gnd_pin.cx()
self.right_gnd_x_center = right_gnd_pin.cx() self.left_vdd_x_center = self.left_vdd_pin.cx()
self.right_vdd_x_center = right_vdd_pin.cx() if right:
self.right_gnd_x_center = self.right_gnd_pin.cx()
self.bottom_gnd_y_center = bottom_gnd_pin.cy() self.right_vdd_x_center = self.right_vdd_pin.cx()
self.bottom_vdd_y_center = bottom_vdd_pin.cy() if bottom:
self.top_gnd_y_center = top_gnd_pin.cy() self.bottom_gnd_y_center = self.bottom_gnd_pin.cy()
self.top_vdd_y_center = top_vdd_pin.cy() self.bottom_vdd_y_center = self.bottom_vdd_pin.cy()
if top:
# Find the number of vias for this pitch self.top_gnd_y_center = self.top_gnd_pin.cy()
self.supply_vias = 1 self.top_vdd_y_center = self.top_vdd_pin.cy()
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)]
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: for pt in via_points:
self.add_via_center(layers=self.m1_stack, self.add_via_stack_center(offset=pt,
offset=pt, from_layer=h_layer,
size=(self.supply_vias, to_layer=v_layer,
self.supply_vias)) min_area=True)
def pdf_write(self, pdf_name): def pdf_write(self, pdf_name):
""" """
Display the layout to a PDF file. 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): if not os.path.exists(self.lvs_file):
self.lvs_file = self.sp_file self.lvs_file = self.sp_file
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
# Holds subckts/mods for this module # Holds subckts/mods for this module
self.mods = set() self.mods = set()
# Holds the pins for this module (in order) # Holds the pins for this module (in order)
@@ -699,7 +700,8 @@ class spice():
def get_instance_connections(self): def get_instance_connections(self):
conns = [] conns = []
for inst in self.insts: for inst in self.insts:
conns.append(inst.get_connections()) if "contact" not in inst.name:
conns.append(inst.get_connections())
return conns return conns
def is_net_alias(self, known_net, net_alias, mod, exclusion_set): def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
+1 -1
View File
@@ -41,7 +41,7 @@ from .local_bitcell_array import *
from .nand2_dec import * from .nand2_dec import *
from .nand3_dec import * from .nand3_dec import *
from .nand4_dec import * from .nand4_dec import *
from .orig_bitcell_array import * #from .orig_bitcell_array import *
from .pand2 import * from .pand2 import *
from .pand3 import * from .pand3 import *
from .pand4 import * from .pand4 import *
+23 -13
View File
@@ -10,7 +10,9 @@ from openram.tech import drc, spice
from openram.sram_factory import factory from openram.sram_factory import factory
from openram import OPTS from openram import OPTS
from .bitcell_base_array import bitcell_base_array from .bitcell_base_array import bitcell_base_array
from .pattern import pattern
from openram.base import geometry, instance
from math import ceil
class bitcell_array(bitcell_base_array): class bitcell_array(bitcell_base_array):
""" """
@@ -42,7 +44,7 @@ class bitcell_array(bitcell_base_array):
def create_layout(self): def create_layout(self):
self.place_array("bit_r{0}_c{1}") self.place_array()
self.add_layout_pins() self.add_layout_pins()
@@ -57,18 +59,26 @@ class bitcell_array(bitcell_base_array):
self.cell = factory.create(module_type=OPTS.bitcell) self.cell = factory.create(module_type=OPTS.bitcell)
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ self.cell_inst={}
self.cell_inst = {} if self.cell.mirror.y:
for col in range(self.column_size): core_block = [[0 for x in range(2)] for y in range(2)]
for row in range(self.row_size): core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True)
name = "bit_r{0}_c{1}".format(row, col) core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX')
self.cell_inst[row, col]=self.add_inst(name=name, core_block[0][1] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY')
mod=self.cell) core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY')
self.connect_inst(self.get_bitcell_pins(row, col)) else:
core_block = [[0 for x in range(1)] for y in range(2)]
core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True)
core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX')
# If it is a "core" cell, it could be trimmed for sim time
if col>0 and col<self.column_size-1 and row>0 and row<self.row_size-1: self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size,name_template="bit_r{0}_c{1}")
self.trim_insts.add(name) self.pattern.connect_array()
for key in self.cell_inst.keys():
(row, col) = key
if col>0 and col<self.column_size-1 and row>0 and row<self.row_size-1:
self.trim_insts.add(self.cell_inst[key].name)
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW.""" """Power of Bitcell array and bitline in nW."""
+12 -32
View File
@@ -6,10 +6,11 @@
# All rights reserved. # All rights reserved.
# #
from openram import debug from openram import debug
from openram.base.geometry import instance
from openram.base import design from openram.base import design
from openram.sram_factory import factory from openram.sram_factory import factory
from openram import OPTS from openram import OPTS
from openram.modules import pattern
class bitcell_base_array(design): class bitcell_base_array(design):
""" """
@@ -149,7 +150,7 @@ class bitcell_base_array(design):
wl_names = self.cell.get_all_wl_names() wl_names = self.cell.get_all_wl_names()
for row in range(self.row_size): for row in range(self.row_size):
for port in self.all_ports: for port in self.all_ports:
wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port]) wl_pin = self.cell_inst[self.row_size - 1 - row, 0].get_pin(wl_names[port])
self.add_layout_pin(text="wl_{0}_{1}".format(port, row), self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
layer=wl_pin.layer, layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1), offset=wl_pin.ll().scale(0, 1),
@@ -157,14 +158,17 @@ class bitcell_base_array(design):
height=wl_pin.height()) height=wl_pin.height())
def route_supplies(self): def route_supplies(self):
for inst in self.cell_inst.values(): for inst in self.insts:
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name) if pin_name in inst.mod.get_pin_names():
self.copy_layout_pin(inst, pin_name)
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
self.add_bitline_pins() if self.get_bitline_names():
self.add_wl_pins() self.add_bitline_pins()
if self.get_wordline_names():
self.add_wl_pins()
def _adjust_x_offset(self, xoffset, col, col_offset): def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset tempx = xoffset
@@ -184,32 +188,8 @@ class bitcell_base_array(design):
dir_x = True dir_x = True
return (tempy, dir_x) return (tempy, dir_x)
def place_array(self, name_template, row_offset=0): def place_array(self):
# We increase it by a well enclosure so the precharges don't overlap our wells self.pattern.place_array()
self.height = self.row_size * self.cell.height
self.width = self.column_size * self.cell.width
xoffset = 0.0
for col in range(self.column_size):
yoffset = 0.0
tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset)
for row in range(self.row_size):
tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset)
if dir_x and dir_y:
dir_key = "XY"
elif dir_x:
dir_key = "MX"
elif dir_y:
dir_key = "MY"
else:
dir_key = ""
self.cell_inst[row, col].place(offset=[tempx, tempy],
mirror=dir_key)
yoffset += self.cell.height
xoffset += self.cell.width
def get_column_offsets(self): def get_column_offsets(self):
""" """
+113 -96
View File
@@ -51,10 +51,10 @@ class capped_replica_bitcell_array(bitcell_base_array):
self.rbls = self.left_rbl + self.right_rbl self.rbls = self.left_rbl + self.right_rbl
# Two dummy rows plus replica even if we don't add the column # Two dummy rows plus replica even if we don't add the column
self.extra_rows = sum(self.rbl) self.extra_rows = sum(self.rbl) + 2
# If we aren't using row/col caps, then we need to use the bitcell # If we aren't using row/col caps, then we need to use the bitcell
if not self.cell.end_caps: #if not self.cell.end_caps:
self.extra_rows += 2 # self.extra_rows += 2
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@@ -85,7 +85,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=self.column_size + len(self.rbls), cols=self.column_size + len(self.rbls),
rows=1, rows=1,
# dummy column + left replica column(s) # dummy column + left replica column(s)
column_offset=1, column_offset=0,
mirror=0, mirror=0,
location="top") location="top")
@@ -93,7 +93,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=self.column_size + len(self.rbls), cols=self.column_size + len(self.rbls),
rows=1, rows=1,
# dummy column + left replica column(s) # dummy column + left replica column(s)
column_offset=1, column_offset=0,
mirror=0, mirror=0,
location="bottom") location="bottom")
@@ -104,7 +104,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=1, cols=1,
column_offset=0, column_offset=0,
rows=self.row_size + self.extra_rows, rows=self.row_size + self.extra_rows,
mirror=(self.rbl[0] + 1) % 2) location="left")
self.row_cap_right = factory.create(module_type=row_cap_module_type, self.row_cap_right = factory.create(module_type=row_cap_module_type,
cols=1, cols=1,
@@ -112,9 +112,9 @@ class capped_replica_bitcell_array(bitcell_base_array):
# + left replica column(s) # + left replica column(s)
# + bitcell columns # + bitcell columns
# + right replica column(s) # + right replica column(s)
column_offset=1 + len(self.left_rbl) + self.column_size + self.rbl[0], column_offset=len(self.left_rbl) + self.column_size + self.rbl[0],
rows=self.row_size + self.extra_rows, rows=self.row_size + self.extra_rows,
mirror=(self.rbl[0] + 1) % 2) location="right")
def add_pins(self): def add_pins(self):
@@ -201,40 +201,34 @@ class capped_replica_bitcell_array(bitcell_base_array):
# row-based or column based power and ground lines. # row-based or column based power and ground lines.
self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0])) self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0]))
self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2])) self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2]))
# FIXME: custom sky130 replica module has a better version of this offset
self.unused_offset = vector(0.25, 0.25)
# This is a bitcell x bitcell offset to scale
self.bitcell_offset = vector(self.cell.width, self.cell.height)
self.col_end_offset = vector(self.cell.width, self.cell.height)
self.row_end_offset = vector(self.cell.width, self.cell.height)
# Everything is computed with the replica array # Everything is computed with the replica array
self.replica_bitcell_array_inst.place(offset=self.unused_offset) self.replica_bitcell_array_inst.place(offset=0)
self.add_end_caps() self.add_end_caps()
# shift everything up and right to account for cap cells ll = vector(-1 * self.dummy_col_insts[0].width, -1 * self.dummy_row_insts[0].height)
self.translate_all(self.bitcell_offset.scale(-1, -1)) self.translate_all(ll)
self.capped_rba_width = self.dummy_col_insts[0].width + self.dummy_row_insts[0].width + self.dummy_col_insts[1].width
self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x self.capped_rba_height = self.dummy_col_insts[0].height
self.height = self.dummy_row_insts[1].uy()
self.add_layout_pins()
#self.route_power_ring(self.supply_stack[2], self.supply_stack[0])
self.route_supplies() self.route_supplies()
self.route_unused_wordlines() self.route_unused_wordlines()
lower_left = self.find_lowest_coords() self.reset_coordinates()
upper_right = self.find_highest_coords() self.add_layout_pins()
self.width = upper_right.x - lower_left.x
self.height = upper_right.y - lower_left.y
self.translate_all(lower_left)
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def route_power_ring(self, v_layer, h_layer):
self.bbox = (vector(0,0), vector(self.capped_rba_width, self.capped_rba_height))
self.supply_rail_width = drc["minwidth_m3"]
self.supply_rail_pitch = 6 * self.supply_rail_width
self.add_power_ring(v_layer=v_layer, h_layer=h_layer)
def get_main_array_top(self): def get_main_array_top(self):
return self.replica_bitcell_array_inst.by() + self.replica_bitcell_array.get_main_array_top() return self.replica_bitcell_array_inst.by() + self.replica_bitcell_array.get_main_array_top()
@@ -248,7 +242,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
def get_main_array_right(self): def get_main_array_right(self):
return self.replica_bitcell_array_inst.lx() + self.replica_bitcell_array.get_main_array_right() return self.replica_bitcell_array_inst.lx() + self.replica_bitcell_array.get_main_array_right()
# FIXME: these names need to be changed to reflect what they're actually returning #FIXME: these names need to be changed to reflect what they're actually returning
def get_replica_top(self): def get_replica_top(self):
return self.dummy_row_insts[1].by() return self.dummy_row_insts[1].by()
@@ -261,7 +255,6 @@ class capped_replica_bitcell_array(bitcell_base_array):
def get_replica_right(self): def get_replica_right(self):
return self.dummy_col_insts[1].rx() return self.dummy_col_insts[1].rx()
def get_column_offsets(self): def get_column_offsets(self):
""" """
Return an array of the x offsets of all the regular bits Return an array of the x offsets of all the regular bits
@@ -273,26 +266,23 @@ class capped_replica_bitcell_array(bitcell_base_array):
def add_end_caps(self): def add_end_caps(self):
""" Add dummy cells or end caps around the array """ """ Add dummy cells or end caps around the array """
# Far top dummy row (first row above array is NOT flipped if even number of rows) # Far top dummy row
flip_dummy = (self.row_size + self.rbl[1]) % 2 offset = self.replica_bitcell_array_inst.ul()
dummy_row_offset = self.bitcell_offset.scale(0, flip_dummy) + self.replica_bitcell_array_inst.ul() self.dummy_row_insts[1].place(offset=offset)
self.dummy_row_insts[1].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# Far bottom dummy row (first row below array IS flipped) # Far bottom dummy row
flip_dummy = (self.rbl[0] + 1) % 2 dummy_row_height = vector(0, self.dummy_row_insts[0].height)
dummy_row_offset = self.bitcell_offset.scale(0, flip_dummy - 1) + self.unused_offset offset = self.replica_bitcell_array_inst.ll() - dummy_row_height
self.dummy_row_insts[0].place(offset=dummy_row_offset, self.dummy_row_insts[0].place(offset=offset)
mirror="MX" if flip_dummy else "R0")
# Far left dummy col # Far left dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array dummy_col_width = vector(self.dummy_col_insts[0].width, 0)
dummy_col_offset = self.bitcell_offset.scale(-1, -1) + self.unused_offset offset = self.dummy_row_insts[0].ll() - dummy_col_width
self.dummy_col_insts[0].place(offset=dummy_col_offset) self.dummy_col_insts[0].place(offset=offset)
# Far right dummy col # Far right dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array offset = self.dummy_row_insts[0].lr()
dummy_col_offset = self.bitcell_offset.scale(0, -1) + self.replica_bitcell_array_inst.lr() self.dummy_col_insts[1].place(offset=offset)
self.dummy_col_insts[1].place(offset=dummy_col_offset)
def add_layout_pins(self): def add_layout_pins(self):
for pin_name in self.used_wordline_names + self.bitline_pin_list: for pin_name in self.used_wordline_names + self.bitline_pin_list:
@@ -301,13 +291,13 @@ class capped_replica_bitcell_array(bitcell_base_array):
if "wl" in pin_name: if "wl" in pin_name:
# wordlines # wordlines
pin_offset = pin.ll().scale(0, 1) pin_offset = pin.ll().scale(0, 1)
pin_width = self.width pin_width = self.capped_rba_width
pin_height = pin.height() pin_height = pin.height()
else: else:
# bitlines # bitlines
pin_offset = pin.ll().scale(1, 0) pin_offset = pin.ll().scale(1, 0)
pin_width = pin.width() pin_width = pin.width()
pin_height = self.height pin_height = self.capped_rba_height
self.add_layout_pin(text=pin_name, self.add_layout_pin(text=pin_name,
layer=pin.layer, layer=pin.layer,
@@ -321,56 +311,83 @@ class capped_replica_bitcell_array(bitcell_base_array):
bitcell = factory.create(module_type="pbitcell") bitcell = factory.create(module_type="pbitcell")
else: else:
bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports))
top = True
bottom = True
left = False
right = False
if top:
inst = self.dummy_row_insts[1]
if "vdd" in inst.mod.pins:
array_pins = inst.get_pins("vdd")
for array_pin in array_pins:
supply_pin = self.top_vdd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
vdd_dir = bitcell.vdd_dir array_pins = inst.get_pins("gnd")
gnd_dir = bitcell.gnd_dir for array_pin in array_pins:
supply_pin = self.top_gnd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
if bottom:
inst = self.dummy_row_insts[0]
if "vdd" in inst.mod.pins:
array_pins = inst.get_pins("vdd")
for array_pin in array_pins:
supply_pin = self.bottom_vdd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
# vdd/gnd are only connected in the perimeter cells array_pins = inst.get_pins("gnd")
supply_insts = self.dummy_col_insts + self.dummy_row_insts for array_pin in array_pins:
supply_pin = self.bottom_gnd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
if left:
inst = self.dummy_col_insts[0]
if "vdd" in inst.mod.pins:
array_pins = inst.get_pins("vdd")
for array_pin in array_pins:
supply_pin = self.left_vdd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
# For the wordlines array_pins = inst.get_pins("gnd")
top_bot_mult = 1 for array_pin in array_pins:
left_right_mult = 1 supply_pin = self.left_gnd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
# There are always vertical pins for the WLs on the left/right if we have unused wordlines self.add_via_stack_center(from_layer = array_pin.layer,
self.left_gnd_locs = self.route_side_pin("gnd", "left", left_right_mult) to_layer = supply_pin.layer,
self.right_gnd_locs = self.route_side_pin("gnd", "right", left_right_mult) offset = vector(supply_pin.center()[0], array_pin.center()[1]))
# This needs to be big enough so that they aren't in the same supply routing grid if right:
left_right_mult = 4 inst = self.dummy_col_insts[1]
if "vdd" in inst.mod.pins:
if gnd_dir == "V": array_pins = inst.get_pins("vdd")
self.top_gnd_locs = self.route_side_pin("gnd", "top", top_bot_mult) for array_pin in array_pins:
self.bot_gnd_locs = self.route_side_pin("gnd", "bot", top_bot_mult) supply_pin = self.right_vdd_pin
# This needs to be big enough so that they aren't in the same supply routing grid self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
top_bot_mult = 4 self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
if vdd_dir == "V": offset = vector(supply_pin.center()[0], array_pin.center()[1]))
self.top_vdd_locs = self.route_side_pin("vdd", "top", top_bot_mult)
self.bot_vdd_locs = self.route_side_pin("vdd", "bot", top_bot_mult)
elif vdd_dir == "H":
self.left_vdd_locs = self.route_side_pin("vdd", "left", left_right_mult)
self.right_vdd_locs = self.route_side_pin("vdd", "right", left_right_mult)
else:
debug.error("Invalid vdd direction {}".format(vdd_dir), -1)
for inst in supply_insts:
for pin in inst.get_pins("vdd"):
if vdd_dir == "V":
self.connect_side_pin(pin, "top", self.top_vdd_locs[0].y)
self.connect_side_pin(pin, "bot", self.bot_vdd_locs[0].y)
elif vdd_dir == "H":
self.connect_side_pin(pin, "left", self.left_vdd_locs[0].x)
self.connect_side_pin(pin, "right", self.right_vdd_locs[0].x)
for inst in supply_insts:
for pin in inst.get_pins("gnd"):
if gnd_dir == "V":
self.connect_side_pin(pin, "top", self.top_gnd_locs[0].y)
self.connect_side_pin(pin, "bot", self.bot_gnd_locs[0].y)
elif gnd_dir == "H":
self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
array_pins = inst.get_pins("gnd")
for array_pin in array_pins:
supply_pin = self.right_gnd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
def route_unused_wordlines(self): def route_unused_wordlines(self):
""" """
Connect the unused RBL and dummy wordlines to gnd Connect the unused RBL and dummy wordlines to gnd
+18 -18
View File
@@ -79,24 +79,24 @@ class col_cap_array(bitcell_base_array):
return bitcell_pins return bitcell_pins
def add_layout_pins(self): # def add_layout_pins(self):
""" Add the layout pins """ # """ Add the layout pins """
column_list = self.cell.get_all_bitline_names() # column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size): # for col in range(self.column_size):
for cell_column in column_list: # for cell_column in column_list:
bl_pin = self.cell_inst[0, col].get_pin(cell_column) # bl_pin = self.cell_inst[0, col].get_pin(cell_column)
self.add_layout_pin(text=cell_column + "_{0}".format(col), # self.add_layout_pin(text=cell_column + "_{0}".format(col),
layer=bl_pin.layer, # layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0), # offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(), # width=bl_pin.width(),
height=self.height) # height=self.height)
# Add vdd/gnd via stacks # # Add vdd/gnd via stacks
for row in range(self.row_size): # for row in range(self.row_size):
for col in range(self.column_size): # for col in range(self.column_size):
inst = self.cell_inst[row, col] # inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]: # for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name): # for pin in inst.get_pins(pin_name):
self.copy_layout_pin(inst, pin_name) # self.copy_layout_pin(inst, pin_name)
+24 -53
View File
@@ -6,16 +6,20 @@
from openram.sram_factory import factory from openram.sram_factory import factory
from openram import OPTS from openram import OPTS
from .bitcell_base_array import bitcell_base_array from .bitcell_base_array import bitcell_base_array
from openram.base import geometry
from .pattern import pattern
class dummy_array(bitcell_base_array): class dummy_array(bitcell_base_array):
""" """
Generate a dummy row/column for the replica array. Generate a dummy row/column for the replica array.
""" """
def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""):
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.mirror = mirror
def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name=""):
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.location = location
self.row_offset = row_offset
self.mirror = mirror
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@@ -32,7 +36,7 @@ class dummy_array(bitcell_base_array):
def create_layout(self): def create_layout(self):
self.place_array("dummy_r{0}_c{1}", self.mirror) self.place_array()
self.add_layout_pins() self.add_layout_pins()
@@ -50,13 +54,21 @@ class dummy_array(bitcell_base_array):
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ """ Create the module instances used in this design """
self.cell_inst = {} self.cell_inst={}
for col in range(self.column_size): if self.cell.mirror.y:
for row in range(self.row_size): core_block = [[0 for x in range(2)] for y in range(2)]
name = "bit_r{0}_c{1}".format(row, col) core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True)
self.cell_inst[row, col]=self.add_inst(name=name, core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX')
mod=self.dummy_cell) core_block[(0+self.mirror) %2][1] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY')
self.connect_inst(self.get_bitcell_pins(row, col)) core_block[(1+self.mirror) %2][1] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY')
else:
core_block = [[0 for x in range(1)] for y in range(2)]
core_block[(0+self.mirror) %2][0] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True)
core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX')
self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size, name_template="bit_r{0}_c{1}")
self.pattern.connect_array()
def add_pins(self): def add_pins(self):
# bitline pins are not added because they are floating # bitline pins are not added because they are floating
@@ -68,47 +80,6 @@ class dummy_array(bitcell_base_array):
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_layout_pins(self):
""" Add the layout pins """
# Add the bitline metal, but not as pins since they are going to just be floating
# For some reason, LVS has an issue if we don't add this metal
bitline_names = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for port in self.all_ports:
bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port])
self.add_layout_pin(text="bl_{0}_{1}".format(port, col),
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1])
self.add_layout_pin(text="br_{0}_{1}".format(port, col),
layer=br_pin.layer,
offset=br_pin.ll().scale(1, 0),
width=br_pin.width(),
height=self.height)
wl_names = self.cell.get_all_wl_names()
for row in range(self.row_size):
for port in self.all_ports:
wl_pins = self.cell_inst[row, 0].get_pins(wl_names[port])
for wl_pin in wl_pins:
self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
def route_supplies(self):
# Copy a vdd/gnd layout pin from every cell
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
def input_load(self): def input_load(self):
# FIXME: This appears to be old code from previous characterization. Needs to be updated. # FIXME: This appears to be old code from previous characterization. Needs to be updated.
wl_wire = self.gen_wl_wire() wl_wire = self.gen_wl_wire()
-116
View File
@@ -1,116 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2024 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram.sram_factory import factory
from openram.tech import drc, spice
from openram import OPTS
from .bitcell_base_array import bitcell_base_array
class bitcell_array(bitcell_base_array):
"""
Creates a rows x cols array of memory cells. Assumes bit-lines
and word line is connected by abutment.
Connects the word lines and bit lines.
"""
def __init__(self, cols, rows, name, column_offset=0):
super().__init__(cols, rows, name, column_offset)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
# We don't offset this because we need to align
# the replica bitcell in the control logic
# self.offset_all_coordinates()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place_array("bit_r{0}_c{1}")
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
""" Add the modules used in this design """
self.cell = factory.create(module_type=OPTS.bitcell)
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
for col in range(self.column_size):
for row in range(self.row_size):
name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.cell)
self.connect_inst(self.get_bitcell_pins(col, row))
def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW."""
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
# Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(corner, load)
# Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
cell_power.leakage * self.column_size * self.row_size)
return total_power
def gen_wl_wire(self):
if OPTS.netlist_only:
width = 0
else:
width = self.width
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1"))
wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
return wl_wire
def gen_bl_wire(self):
if OPTS.netlist_only:
height = 0
else:
height = self.height
bl_pos = 0
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1"))
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
return bl_wire
def get_wordline_cin(self):
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
bitcell_wl_cin = self.cell.get_wl_cin()
total_cin = bitcell_wl_cin * self.column_size
return total_cin
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
# Function is not robust with column mux configurations
for row in range(self.row_size):
for col in range(self.column_size):
if row == targ_row and col == targ_col:
continue
self.graph_inst_exclude.add(self.cell_inst[row, col])
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
return inst_name + "{}x".format(OPTS.hier_seperator) + self.cell_inst[row, col].name, self.cell_inst[row, col]
+223
View File
@@ -0,0 +1,223 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
# All rights reserved.
#
from openram import debug
from openram.base.geometry import instance,geometry
from typing import List
from typing import Optional
from openram.base import design
from openram.globals import OPTS
from math import ceil, floor
from copy import deepcopy
class pattern():
"""
This class is used to desribe the internals of a bitcell array. It describes
instance modules, rotation, and ordering
"""
block = List[List[instance]]
def __init__(self,
parent_design: design,
name:str,
core_block:block,
num_rows:int,
num_cols:int,
name_template,
num_cores_x: Optional[int] = 0,
num_cores_y: Optional[int] = 0,
cores_per_x_block: int = 1,
cores_per_y_block: int = 1,
x_block: Optional[block] = None,
y_block: Optional[block] = None,
xy_block: Optional[block] = None,
initial_x_block:bool = False,
initial_y_block:bool = False,
final_x_block:bool = False,
final_y_block:bool = False,
):
"""
a "block" is a 2d list of instances
core_block defines the main block that is tiled
num_core defines the number of times the core block is to be tiled
(i.e. bitcells in dimension / bitcells in core_block)
x_block defines a block that is inserted to the right of the core_block
y_block defines a block that is inserted below the core block
xy_block defines a block that is a inserted where the x_block and y_block intercept
the initial and final booleans determine whether the pattern begins and/or ends with x/y blocks
"""
self.parent_design = parent_design
self.name = name
self.core_block = core_block
self.num_rows = num_rows
self.num_cols = num_cols
self.name_template = name_template
self.num_cores_x = num_cores_x
self.num_cores_y = num_cores_y
if num_cores_x == 0:
self.num_cores_x = ceil(num_cols/len(core_block[0]))
if num_cores_y == 0:
self.num_cores_y = ceil(num_rows/len(core_block))
self.cores_per_x_block = cores_per_x_block
self.cores_per_y_block = cores_per_y_block
self.x_block = x_block
self.y_block = y_block
self.xy_block = xy_block
self.initial_x_block = initial_x_block
self.initial_y_block = initial_y_block
self.final_x_block = final_x_block
self.final_y_block = final_y_block
self.bits_per_row = ceil(self.num_rows/self.num_cores_x)
self.bits_per_col = ceil(self.num_cols/self.num_cores_y)
self.bit_rows = []
self.bit_cols = []
self.parent_design.all_inst = {}
if not OPTS.netlist_only:
self.verify_interblock_dimensions()
def compute_and_verify_intrablock_dimensions(self, block: block) -> List[int]:
for row in block:
row_height = row[0].height
for inst in row:
debug.check(row_height == inst.height, "intrablock instances within the same row are different heights")
for y in range(len(block[0])):
debug.check(all([row[y].width for row in block]), "intrablock instances within the same column are different widths")
block_width = sum([instance.width for instance in block[0]])
block_height = sum([row[0].height for row in block])
return [block_width, block_height]
def verify_interblock_dimensions(self) -> None:
"""
Ensure the individual blocks are valid and interblock dimensions are valid
"""
debug.check(len(self.core_block) >= 1, "invalid core_block dimension: core_block rows must be >=1")
debug.check(len(self.core_block[0]) >= 1, "invalid core_block dimension: core_block cols must be >=1")
if self.x_block and self.y_block:
debug.check(self.xy_block is not None, "must have xy_block if both x_block and y_block are provided")
(self.core_block_width, self.core_block_height) = self.compute_and_verify_intrablock_dimensions(self.core_block)
if self.x_block:
(self.x_block_width, self.x_block_height) = self.compute_and_verify_intrablock_dimensions(self.x_block)
if self.y_block:
(self.y_block_width, self.y_block_height) = self.compute_and_verify_intrablock_dimensions(self.y_block)
if self.xy_block:
(self.xy_block_width, self.xy_block_height) = self.compute_and_verify_intrablock_dimensions(self.xy_block)
if(self.x_block):
debug.check(self.core_block_width * self.cores_per_x_block == self.x_block_width, "core_block does not align with x_block")
if(self.y_block):
debug.check(self.core_block_height * self.cores_per_y_block == self.y_block_height, "core_block does not aligns with y_block")
if(self.xy_block):
debug.check(self.xy_block_height == self.x_block_height, "xy_block does not align with x_block")
debug.check(self.xy_block_width == self.y_block_width, "xy_block does not align with y_block")
def connect_block(self, block: block, col: int, row: int):
for dr in range(len(block)):
row_done = False
for dc in range(len(block[0])):
if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.num_cols) <= self.num_rows):
inst = block[dr][dc]
if(len(self.bit_rows) <= col + dc):
self.bit_rows.append(0)
if(len(self.bit_cols) <= row + dr):
self.bit_cols.append(0)
if(row_done or self.bit_cols[row+dr] >= self.num_cols):
row_done = True
continue
if((self.bit_rows[col+dc] < self.num_rows) and (self.bit_cols[row+dr] < self.num_cols)):
if(inst.is_bitcell):
self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc))
self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]]
self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col+dc], self.bit_cols[row+dr]))
self.bit_rows[col+dc] += 1
self.bit_cols[row+dr] += 1
else:
self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc))
self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col+dc], self.bit_cols[row+dr]))
else:
row_done = True
def connect_array(self) -> None:
#debug_array = [[None]*12 for _ in range(6)]
row = 0
col = 0
for i in range(self.num_cores_y):
for j in range (self.num_cores_x):
self.connect_block(self.core_block, col, row)
col += len(self.core_block[0])
col = 0
row += len(self.core_block)
def connect_array_raw(self) -> None:
for row in range(self.num_rows):
for col in range(self.num_cols):
inst = self.core_block[row][col]
if(len(self.bit_rows) <= col):
self.bit_rows.append(0)
if(len(self.bit_cols) <= row):
self.bit_cols.append(0)
if(inst.is_bitcell):
self.parent_design.cell_inst[self.bit_rows[col], self.bit_cols[row]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row, col))
self.parent_design.all_inst[row, col] = self.parent_design.cell_inst[self.bit_rows[col], self.bit_cols[row]]
self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col], self.bit_cols[row]))
self.bit_rows[col] += 1
self.bit_cols[row] += 1
else:
self.parent_design.all_inst[row, col] = self.parent_design.add_existing_inst(inst,self.name_template.format(row, col))
self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col], self.bit_cols[row]))
def print_bit_blocck():
return
def place_inst(self, inst, offset) -> None:
x = offset[0]
y = offset[1]
if "X" in inst.mirror:
y += inst.height
if "Y" in inst.mirror:
x += inst.width
#print('placing inst {} at {}'.format(inst, offset))
inst.place((x, y), inst.mirror, inst.rotate)
def place_array(self):
(self.row_max, self.col_max) = list(self.parent_design.all_inst.keys())[-1]
y = 0
for row in range(self.row_max+1):
x = 0
for col in range(self.col_max+1):
inst = self.parent_design.all_inst[self.row_max - row, col]
self.place_inst(inst, (x, y))
x += inst.width
y += inst.height
self.parent_design.width = max([x.rx() for x in self.parent_design.insts])
self.parent_design.height = max([x.uy() for x in self.parent_design.insts])
def append_row_to_block(block, row):
block.append(row)
def append_block_under_block(base_block, under_block):
base_block = base_block + under_block
def append_block_right_block(base_block, right_block):
for row in base_block:
row = row + right_block
def rotate_list(lst, n):
# Loop through the range of n positions to rotate
for i in range(n):
# Remove the last element of the list and insert it at the beginning
lst.insert(0, lst.pop())
# Return the rotated list
return lst
+7 -5
View File
@@ -156,13 +156,15 @@ class pgate(design):
return via return via
def extend_wells(self): def extend_wells(self, width=None):
""" Extend the n/p wells to cover whole cell """ """ Extend the n/p wells to cover whole cell """
# This should match the cells in the cell library # This should match the cells in the cell library
self.nwell_yoffset = 0.48 * self.height self.nwell_yoffset = 0.48 * self.height
full_height = self.height + 0.5 * self.m1_width full_height = self.height + 0.5 * self.m1_width
if width == None:
width = self.width + 2 * self.well_extend_active
# FIXME: float rounding problem # FIXME: float rounding problem
if "nwell" in layer: if "nwell" in layer:
@@ -173,12 +175,12 @@ class pgate(design):
nwell_height = nwell_max_offset - self.nwell_yoffset nwell_height = nwell_max_offset - self.nwell_yoffset
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=nwell_position, offset=nwell_position,
width=self.width + 2 * self.well_extend_active, width=width,
height=nwell_height) height=nwell_height)
if "vtg" in layer: if "vtg" in layer:
self.add_rect(layer="vtg", self.add_rect(layer="vtg",
offset=nwell_position, offset=nwell_position,
width=self.width + 2 * self.well_extend_active, width=width,
height=nwell_height) height=nwell_height)
# Start this half a rail width below the cell # Start this half a rail width below the cell
@@ -189,12 +191,12 @@ class pgate(design):
pwell_height = self.nwell_yoffset - pwell_position.y pwell_height = self.nwell_yoffset - pwell_position.y
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=pwell_position, offset=pwell_position,
width=self.width + 2 * self.well_extend_active, width=width,
height=pwell_height) height=pwell_height)
if "vtg" in layer: if "vtg" in layer:
self.add_rect(layer="vtg", self.add_rect(layer="vtg",
offset=pwell_position, offset=pwell_position,
width=self.width + 2 * self.well_extend_active, width=width,
height=pwell_height) height=pwell_height)
if cell_props.pgate.add_implants: if cell_props.pgate.add_implants:
+49 -43
View File
@@ -4,6 +4,8 @@
# All rights reserved. # All rights reserved.
# #
from openram import debug from openram import debug
from openram.base import round_to_grid
from openram.tech import drc
from openram.base import vector from openram.base import vector
from openram.base import contact from openram.base import contact
from openram.sram_factory import factory from openram.sram_factory import factory
@@ -74,7 +76,7 @@ class replica_bitcell_array(bitcell_base_array):
""" Array and dummy/replica columns """ """ Array and dummy/replica columns """
# Bitcell array # Bitcell array
self.bitcell_array = factory.create(module_type="bitcell_array", self.bitcell_array = factory.create(module_type="bitcell_array",
column_offset=1 + len(self.left_rbl), column_offset=len(self.left_rbl),
cols=self.column_size, cols=self.column_size,
rows=self.row_size) rows=self.row_size)
@@ -87,11 +89,11 @@ class replica_bitcell_array(bitcell_base_array):
if port in self.left_rbl: if port in self.left_rbl:
# These go top down starting from the bottom of the bitcell array. # These go top down starting from the bottom of the bitcell array.
replica_bit = self.rbl[0] - port - 1 replica_bit = self.rbl[0] - port - 1
column_offset = len(self.left_rbl) column_offset = 0
elif port in self.right_rbl: elif port in self.right_rbl:
# These go bottom up starting from the top of the bitcell array. # These go bottom up starting from the top of the bitcell array.
replica_bit = self.rbl[0] + self.row_size + port - 1 replica_bit = self.rbl[0] + self.row_size + port - 1
column_offset = len(self.left_rbl) + self.column_size + 1 column_offset = len(self.left_rbl) + self.column_size
else: else:
continue continue
@@ -102,14 +104,24 @@ class replica_bitcell_array(bitcell_base_array):
replica_bit=replica_bit) replica_bit=replica_bit)
# Dummy row (for replica wordlines) # Dummy row (for replica wordlines)
self.dummy_row = factory.create(module_type="dummy_array", self.dummy_rows = {}
for port in self.all_ports:
if port in self.left_rbl:
row_offset = self.row_size
mirror = row_offset % 2 + 1
elif port in self.right_rbl:
row_offset = 0
mirror = 0
else:
continue
self.dummy_rows[port] = factory.create(module_type="dummy_array",
cols=self.column_size, cols=self.column_size,
rows=1, rows=1,
# cap column + left replica column row_offset=row_offset,
# FIXME: these col offsets should really start at 0 because column_offset=len(self.left_rbl),
# this is the left edge of the array... but changing them all is work mirror=mirror)
column_offset=1 + len(self.left_rbl),
mirror=0)
def add_pins(self): def add_pins(self):
@@ -213,7 +225,7 @@ class replica_bitcell_array(bitcell_base_array):
for port in self.all_ports: # TODO: tie to self.rbl or whatever for port in self.all_ports: # TODO: tie to self.rbl or whatever
if self.rbl[port] != 0: if self.rbl[port] != 0:
self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port),
mod=self.dummy_row)) mod=self.dummy_rows[port]))
self.connect_inst(self.all_bitline_names + self.rbl_wordline_names[port] + self.supplies) self.connect_inst(self.all_bitline_names + self.rbl_wordline_names[port] + self.supplies)
else: else:
self.dummy_row_replica_insts.append(None) self.dummy_row_replica_insts.append(None)
@@ -225,28 +237,28 @@ class replica_bitcell_array(bitcell_base_array):
self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0])) self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0]))
self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2])) self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2]))
# This is a bitcell x bitcell offset to scale
self.bitcell_offset = vector(self.cell.width, self.cell.height)
self.col_end_offset = vector(self.cell.width, self.cell.height)
self.row_end_offset = vector(self.cell.width, self.cell.height)
# Everything is computed with the main array # Everything is computed with the main array
self.bitcell_array_inst.place(offset=0) self.bitcell_array_inst.place(offset=(0,0))
self.add_replica_columns() self.add_replica_columns()
# Array was at (0, 0) but move everything so it is at the lower left # Array was at (0, 0) but move everything so it is at the lower left
# We move DOWN the number of left RBL even if we didn't add the column to this bitcell array # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array
# Note that this doesn't include the row/col cap # Note that this doesn't include the row/col cap
array_offset = self.bitcell_offset.scale(-len(self.left_rbl), -self.rbl[0])
self.translate_all(array_offset) #rbc_width = (self.replica_col_insts[0].width, 0)
#dummy_height = max(x for x in map(lambda x: x if x != None else 0, self.))
#array_offset = self.bitcell_offset.scale(-len(self.left_rbl), -self.rbl[0])
ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts]))
self.translate_all(ll)
self.add_layout_pins() self.add_layout_pins()
self.route_supplies() self.route_supplies()
self.height = (sum(self.rbl) + self.row_size) * self.cell.height self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts])
self.width = (len(self.rbls) + self.column_size) * self.cell.width self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts])
self.add_boundary() self.add_boundary()
@@ -280,25 +292,24 @@ class replica_bitcell_array(bitcell_base_array):
# Grow from left to right, toward the array # Grow from left to right, toward the array
for bit, port in enumerate(self.left_rbl): for bit, port in enumerate(self.left_rbl):
offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0]) rbc_dimension = vector(self.replica_col_insts[port].width, self.cell.height)
offset = rbc_dimension.scale(-len(self.left_rbl) + bit, -self.rbl[0])
self.replica_col_insts[bit].place(offset) self.replica_col_insts[bit].place(offset)
# Grow to the right of the bitcell array, array outward # Grow to the right of the bitcell array, array outward
for bit, port in enumerate(self.right_rbl): for bit, port in enumerate(self.right_rbl):
offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0]) offset = self.bitcell_array_inst.lr() + rbc_dimension.scale(bit, -self.rbl[0])
self.replica_col_insts[self.rbl[0] + bit].place(offset) self.replica_col_insts[self.rbl[0] + bit].place(offset)
# Replica dummy rows # Replica dummy rows
# Add the dummy rows even if we aren't adding the replica column to this bitcell array # Add the dummy rows even if we aren't adding the replica column to this bitcell array
# These grow up, toward the array # These grow up, toward the array
for bit in range(self.rbl[0]): for bit in range(self.rbl[0]):
dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) dummy_offset = self.bitcell_array_inst.ll() - vector(0, self.dummy_row_replica_insts[bit].height)
self.dummy_row_replica_insts[bit].place(offset=dummy_offset, self.dummy_row_replica_insts[bit].place(offset=dummy_offset)
mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0")
# These grow up, away from the array # These grow up, away from the array
for bit in range(self.rbl[1]): for bit in range(self.rbl[1]):
dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() dummy_offset = self.bitcell_array_inst.ul()
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset)
mirror="MX" if (self.row_size + bit) % 2 else "R0")
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
@@ -316,14 +327,15 @@ class replica_bitcell_array(bitcell_base_array):
# Replica wordlines (go by the row instead of replica column because we may have to add a pin # Replica wordlines (go by the row instead of replica column because we may have to add a pin
# even though the column is in another local bitcell array) # even though the column is in another local bitcell array)
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): if self.rbl != [0,0]:
for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
pin = inst.get_pin(pin_name) for (wl_name, pin_name) in zip(names, self.dummy_rows[0].get_wordline_names()):
self.add_layout_pin(text=wl_name, pin = inst.get_pin(pin_name)
layer=pin.layer, self.add_layout_pin(text=wl_name,
offset=pin.ll().scale(0, 1), layer=pin.layer,
width=self.width, offset=pin.ll().scale(0, 1),
height=pin.height()) width=self.width,
height=pin.height())
# Main array bl/br # Main array bl/br
for pin_name in self.all_bitline_names: for pin_name in self.all_bitline_names:
@@ -347,12 +359,6 @@ class replica_bitcell_array(bitcell_base_array):
width=pin.width(), width=pin.width(),
height=self.height) height=self.height)
def route_supplies(self):
""" just copy supply pins from all instances """
for inst in self.insts:
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW.""" """Power of Bitcell array and bitline in nW."""
# Dynamic Power from Bitline # Dynamic Power from Bitline
+28 -98
View File
@@ -9,7 +9,8 @@ from openram.sram_factory import factory
from openram.tech import layer_properties as layer_props from openram.tech import layer_properties as layer_props
from openram import OPTS from openram import OPTS
from .bitcell_base_array import bitcell_base_array from .bitcell_base_array import bitcell_base_array
from openram.base import geometry
from openram.modules import pattern
class replica_column(bitcell_base_array): class replica_column(bitcell_base_array):
""" """
@@ -44,10 +45,10 @@ class replica_column(bitcell_base_array):
debug.check(replica_bit < self.row_start or replica_bit >= self.row_end, debug.check(replica_bit < self.row_start or replica_bit >= self.row_end,
"Replica bit cannot be in the regular array.") "Replica bit cannot be in the regular array.")
if layer_props.replica_column.even_rows: #if layer_props.replica_column.even_rows:
debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0, # debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0,
"sky130 currently requires rows to be even and to start with X mirroring" # "sky130 currently requires rows to be even and to start with X mirroring"
+ " (left_rbl must be odd) for LVS.") # + " (left_rbl must be odd) for LVS.")
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@@ -59,10 +60,7 @@ class replica_column(bitcell_base_array):
self.create_instances() self.create_instances()
def create_layout(self): def create_layout(self):
self.place_instances() self.place_array()
self.height = self.cell_inst[-1].uy()
self.width = self.cell_inst[0].rx()
self.add_layout_pins() self.add_layout_pins()
@@ -88,103 +86,35 @@ class replica_column(bitcell_base_array):
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell) self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
def create_instances(self): def create_instances(self):
self.cell_inst = [] self.cell_inst = {}
core_block = [[0 for x in range(1)] for y in range(self.total_size)]
current_row = self.row_start
for row in range(self.total_size): for row in range(self.total_size):
name = "rbc_{0}".format(row)
# Regular array cells are replica cells # Regular array cells are replica cells
# Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell.
# All other cells are dummies # All other cells are dummies
if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end):
self.cell_inst.append(self.add_inst(name=name, if current_row % 2 == 0:
mod=self.replica_cell)) core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True)
self.connect_inst(self.get_bitcell_pins(row, 0)) else:
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX')
else: else:
self.cell_inst.append(self.add_inst(name=name, if current_row % 2 == 0:
mod=self.dummy_cell)) core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True)
self.connect_inst(self.get_bitcell_pins(row, 0)) else:
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX')
def place_instances(self): current_row += 1
# Flip the mirrors if we have an odd number of replica+dummy rows at the bottom if self.cell.mirror.y:
# so that we will start with mirroring rather than not mirroring
rbl_offset = (self.left_rbl) % 2
# if our bitcells are mirrored on the y axis, check if we are in global
# column that needs to be flipped.
dir_y = False
xoffset = 0
if self.cell.mirror.y and self.column_offset % 2:
dir_y = True
xoffset = self.replica_cell.width
for row in range(self.total_size):
# name = "bit_r{0}_{1}".format(row, "rbl")
dir_x = self.cell.mirror.x and (row + rbl_offset) % 2
offset = vector(xoffset, self.cell.height * (row + (row + rbl_offset) % 2))
if dir_x and dir_y:
dir_key = "XY"
elif dir_x:
dir_key = "MX"
elif dir_y:
dir_key = "MY"
else:
dir_key = ""
self.cell_inst[row].place(offset=offset,
mirror=dir_key)
def add_layout_pins(self):
for port in self.all_ports:
bl_pin = self.cell_inst[0].get_pin(self.cell.get_bl_name(port))
self.add_layout_pin(text="bl_{0}_{1}".format(port, 0),
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
bl_pin = self.cell_inst[0].get_pin(self.cell.get_br_name(port))
self.add_layout_pin(text="br_{0}_{1}".format(port, 0),
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
for port in self.all_ports:
for row in range(self.total_size): for row in range(self.total_size):
wl_pin = self.cell_inst[row].get_pin(self.cell.get_wl_name(port)) if self.column_offset % 2 == 0:
self.add_layout_pin(text="wl_{0}_{1}".format(port, row), if core_block[row][0].mirror=='MX':
layer=wl_pin.layer, core_block[row][0].mirror='XY'
offset=wl_pin.ll().scale(0, 1), else:
width=self.width, core_block[row][0].mirror='MY'
height=wl_pin.height()) self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.total_size, num_cols=self.column_size, name_template="rbc_r{0}_c{1}")
self.pattern.connect_array()
def route_supplies(self):
for inst in self.cell_inst:
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
def get_bitline_names(self, port=None):
if port == None:
return self.all_bitline_names
else:
return self.bitline_names[port]
def get_bitcell_pins(self, row, col):
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = []
for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))])
bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))])
bitcell_pins.append("vdd")
bitcell_pins.append("gnd")
return bitcell_pins
def get_bitcell_pins_col_cap(self, row, col): def get_bitcell_pins_col_cap(self, row, col):
""" """
@@ -208,4 +138,4 @@ class replica_column(bitcell_base_array):
for row, cell in enumerate(self.cell_inst): for row, cell in enumerate(self.cell_inst):
if row != self.replica_bit: if row != self.replica_bit:
self.graph_inst_exclude.add(cell) self.graph_inst_exclude.add(self.cell_inst[cell])
+10 -32
View File
@@ -12,10 +12,11 @@ class row_cap_array(bitcell_base_array):
""" """
Generate a dummy row/column for the replica array. Generate a dummy row/column for the replica array.
""" """
def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""):
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.mirror = mirror self.mirror = mirror
self.no_instances = True self.location = location
#self.no_instances = True
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@@ -69,44 +70,21 @@ class row_cap_array(bitcell_base_array):
return bitcell_pins return bitcell_pins
def place_array(self, name_template, row_offset=0):
xoffset = 0.0
for col in range(self.column_size):
yoffset = self.cell.height
tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset)
for row in range(self.row_size):
tempy, dir_x = self._adjust_y_offset(yoffset, row + 1, row_offset)
if dir_x and dir_y:
dir_key = "XY"
elif dir_x:
dir_key = "MX"
elif dir_y:
dir_key = "MY"
else:
dir_key = ""
self.cell_inst[row, col].place(offset=[tempx, tempy],
mirror=dir_key)
yoffset += self.cell.height
xoffset += self.cell.width
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
row_list = self.cell.get_all_wl_names() wl_names = self.cell.get_all_wl_names()
max_row = self.row_size - 2
for row in range(1, self.row_size - 1): for row in range(0, max_row):
for cell_row in row_list: for port in self.all_ports:
wl_pin = self.cell_inst[row, 0].get_pin(cell_row) wl_pin = self.cell_inst[max_row - 1 - row, 0].get_pin(wl_names[port])
self.add_layout_pin(text=cell_row + "_{0}".format(row), self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
layer=wl_pin.layer, layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1), offset=wl_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=wl_pin.height()) height=wl_pin.height())
for row in range(1, self.row_size - 1): for row in range(0, max_row):
for col in range(self.column_size): for col in range(self.column_size):
inst = self.cell_inst[row, col] inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
+4 -1
View File
@@ -662,7 +662,10 @@ class sram_1bank(design, verilog, lef):
inputs = [] inputs = []
outputs = [] outputs = []
for bit in range(self.num_spare_cols): for bit in range(self.num_spare_cols):
inputs.append("spare_wen{}[{}]".format(port, bit)) if self.num_spare_cols == 1:
inputs.append("spare_wen{}".format(port))
else:
inputs.append("spare_wen{}[{}]".format(port, bit))
outputs.append("bank_spare_wen{}_{}".format(port, bit)) outputs.append("bank_spare_wen{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
+5
View File
@@ -11,6 +11,7 @@ from openram.base import design
from openram.base import vector from openram.base import vector
from openram.sram_factory import factory from openram.sram_factory import factory
from openram import OPTS from openram import OPTS
from openram.tech import drc
class write_mask_and_array(design): class write_mask_and_array(design):
@@ -104,6 +105,10 @@ class write_mask_and_array(design):
base = vector(self.offsets[int(i * write_bits)], 0) base = vector(self.offsets[int(i * write_bits)], 0)
self.and2_insts[i].place(base) self.and2_insts[i].place(base)
# decide whethre to connect nwell to avoid drc errors
if self.bitcell.width < drc("nwell_to_nwell"):
self.and2.extend_wells(width=self.and2.width + drc("nwell_to_nwell"))
def add_layout_pins(self): def add_layout_pins(self):
# Create the enable pin that connects all write mask AND array's B pins # Create the enable pin that connects all write mask AND array's B pins
@@ -25,7 +25,7 @@ class sram_1bank_nomux_func_test(openram_test):
OPTS.analytical_delay = False OPTS.analytical_delay = False
OPTS.netlist_only = True OPTS.netlist_only = True
OPTS.trim_netlist = False OPTS.trim_netlist = False
OPTS.num_sim_threads = 1
# This is a hack to reload the characterizer __init__ with the spice version # This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload from importlib import reload
from openram import characterizer from openram import characterizer
+1
View File
@@ -1,3 +1,4 @@
SHELL := /bin/bash
TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))../..) TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))../..)
include $(TOP_DIR)/openram.mk include $(TOP_DIR)/openram.mk
+2 -2
View File
@@ -7,8 +7,8 @@ CONDA_HOME="${CONDA_HOME:-miniconda}"
# If you want to use the latest version, just use "<tool>". # If you want to use the latest version, just use "<tool>".
TOOLS="" TOOLS=""
TOOLS+="klayout=0.28.3 " TOOLS+="klayout=0.28.3 "
TOOLS+="magic=8.3.363 " TOOLS+="magic=8.3.497 "
TOOLS+="netgen=1.5.253 " TOOLS+="netgen=1.5.286 "
TOOLS+="ngspice=26 " TOOLS+="ngspice=26 "
TOOLS+="trilinos=12.12.1=1 " TOOLS+="trilinos=12.12.1=1 "
TOOLS+="xyce=7.4=3" TOOLS+="xyce=7.4=3"
+3
View File
@@ -65,6 +65,9 @@ all: | configs
example: $(EXAMPLE_STAMPS) example: $(EXAMPLE_STAMPS)
.PHONY: example .PHONY: example
sp: sky130_sram_1kbyte_1rw_32x256_8 sky130_sram_2kbyte_1rw_32x512_8 sky130_sram_4kbyte_1rw_32x1024_8 sky130_sram_4kbyte_1rw_64x512_8
.PHONY: sp
sky130: $(SKY130_STAMPS) sky130: $(SKY130_STAMPS)
.PHONY: sky130 .PHONY: sky130
@@ -1,731 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
# All rights reserved.
#
from openram import debug
from openram.base import vector
from openram.base import contact
from openram.sram_factory import factory
from openram.tech import drc, spice
from openram.tech import cell_properties as props
from openram import OPTS
from openram.modules import bitcell_base_array
class replica_bitcell_array(bitcell_base_array):
"""
Creates a bitcell array of cols x rows and then adds the replica
and dummy columns and rows. Replica columns are on the left and
right, respectively and connected to the given bitcell ports.
Dummy are the outside columns/rows with WL and BL tied to gnd.
Requires a regular bitcell array, replica bitcell, and dummy
bitcell (BL/BR disconnected).
"""
def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""):
super().__init__(name=name, rows=rows, cols=cols, column_offset=0)
debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name,
rows,
cols,
rbl,
left_rbl,
right_rbl))
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.add_comment("rbl: {0} left_rbl: {1} right_rbl: {2}".format(rbl, left_rbl, right_rbl))
self.column_size = cols
self.row_size = rows
# This is how many RBLs are in all the arrays
if rbl:
self.rbl = rbl
else:
self.rbl=[1, 1 if len(self.all_ports)>1 else 0]
# This specifies which RBL to put on the left or right
# by port number
# This could be an empty list
if left_rbl != None:
self.left_rbl = left_rbl
else:
self.left_rbl = [0]
# This could be an empty list
if right_rbl != None:
self.right_rbl = right_rbl
else:
self.right_rbl=[1] if len(self.all_ports) > 1 else []
self.rbls = self.left_rbl + self.right_rbl
debug.check(sum(self.rbl) == len(self.all_ports),
"Invalid number of RBLs for port configuration.")
debug.check(sum(self.rbl) >= len(self.left_rbl) + len(self.right_rbl),
"Invalid number of RBLs for port configuration.")
# Two dummy rows plus replica even if we don't add the column
self.extra_rows = sum(self.rbl)
# Two dummy cols plus replica if we add the column
self.extra_cols = len(self.left_rbl) + len(self.right_rbl)
# If we aren't using row/col caps, then we need to use the bitcell
if not self.cell.end_caps:
self.extra_rows += 2
self.extra_cols += 2
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
# We don't offset this because we need to align
# the replica bitcell in the control logic
# self.offset_all_coordinates()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
self.add_pins()
self.create_instances()
def add_modules(self):
""" Array and dummy/replica columns
d or D = dummy cell (caps to distinguish grouping)
r or R = replica cell (caps to distinguish grouping)
b or B = bitcell
replica columns 1
v v
bdDDDDDDDDDDDDDDdb <- Dummy row
bdDDDDDDDDDDDDDDrb <- Dummy row
br--------------rb
br| Array |rb
br| row x col |rb
br--------------rb
brDDDDDDDDDDDDDDdb <- Dummy row
bdDDDDDDDDDDDDDDdb <- Dummy row
^^^^^^^^^^^^^^^
dummy rows cols x 1
^ dummy columns ^
1 x (rows + 4)
"""
# Bitcell array
self.bitcell_array = factory.create(module_type="bitcell_array",
column_offset=1 + len(self.left_rbl),
cols=self.column_size,
rows=self.row_size)
# Replica bitlines
self.replica_columns = {}
for port in self.all_ports:
if port in self.left_rbl:
# We will always have self.rbl[0] rows of replica wordlines below
# the array.
# These go from the top (where the bitcell array starts ) down
replica_bit = self.rbl[0] - port
column_offset = self.rbl[0]
elif port in self.right_rbl:
# We will always have self.rbl[0] rows of replica wordlines below
# the array.
# These go from the bottom up
replica_bit = self.rbl[0] + self.row_size + port
column_offset = self.rbl[0] + self.column_size + 1
else:
continue
self.replica_columns[port] = factory.create(module_type="replica_column",
rows=self.row_size,
rbl=self.rbl,
column_offset=column_offset,
replica_bit=replica_bit)
# Dummy row
self.dummy_row = factory.create(module_type="dummy_array",
cols=self.column_size,
rows=1,
# dummy column + left replica column
column_offset=1 + len(self.left_rbl),
mirror=0)
# Dummy Row or Col Cap, depending on bitcell array properties
col_cap_module_type = ("col_cap_array" if self.cell.end_caps else "dummy_array")
self.col_cap_top = factory.create(module_type=col_cap_module_type,
cols=self.column_size,
rows=1,
# dummy column + left replica column(s)
column_offset=1 + len(self.left_rbl),
mirror=0,
location="top")
self.col_cap_bottom = factory.create(module_type=col_cap_module_type,
cols=self.column_size,
rows=1,
# dummy column + left replica column(s)
column_offset=1 + len(self.left_rbl),
mirror=0,
location="bottom")
# Dummy Col or Row Cap, depending on bitcell array properties
row_cap_module_type = ("row_cap_array" if self.cell.end_caps else "dummy_array")
self.row_cap_left = factory.create(module_type=row_cap_module_type,
cols=1,
column_offset=0,
rows=self.row_size + self.extra_rows,
mirror=(self.rbl[0] + 1) % 2)
self.row_cap_right = factory.create(module_type=row_cap_module_type,
cols=1,
# dummy column
# + left replica column(s)
# + bitcell columns
# + right replica column(s)
column_offset=1 + len(self.left_rbl) + self.column_size + self.rbl[0],
rows=self.row_size + self.extra_rows,
mirror=(self.rbl[0] + 1) %2)
def add_pins(self):
# Arrays are always:
# bitlines (column first then port order)
# word lines (row first then port order)
# dummy wordlines
# replica wordlines
# regular wordlines (bottom to top)
# # dummy bitlines
# replica bitlines (port order)
# regular bitlines (left to right port order)
#
# vdd
# gnd
self.add_bitline_pins()
self.add_wordline_pins()
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_bitline_pins(self):
# The bit is which port the RBL is for
for bit in self.rbls:
for port in self.all_ports:
self.rbl_bitline_names[bit].append("rbl_bl_{0}_{1}".format(port, bit))
for port in self.all_ports:
self.rbl_bitline_names[bit].append("rbl_br_{0}_{1}".format(port, bit))
# Make a flat list too
self.all_rbl_bitline_names = [x for sl in self.rbl_bitline_names for x in sl]
self.bitline_names = self.bitcell_array.bitline_names
# Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
for port in self.left_rbl:
self.add_pin_list(self.rbl_bitline_names[port], "INOUT")
self.add_pin_list(self.all_bitline_names, "INOUT")
for port in self.right_rbl:
self.add_pin_list(self.rbl_bitline_names[port], "INOUT")
def add_wordline_pins(self):
# Wordlines to ground
self.gnd_wordline_names = []
for port in self.all_ports:
for bit in self.all_ports:
self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
if bit != port:
self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit))
self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl]
self.wordline_names = self.bitcell_array.wordline_names
self.all_wordline_names = self.bitcell_array.all_wordline_names
# All wordlines including dummy and RBL
self.replica_array_wordline_names = []
self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names()))
for bit in range(self.rbl[0]):
self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]])
self.replica_array_wordline_names.extend(self.all_wordline_names)
for bit in range(self.rbl[1]):
self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]])
self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names()))
for port in range(self.rbl[0]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
self.add_pin_list(self.all_wordline_names, "INPUT")
for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
def create_instances(self):
""" Create the module instances used in this design """
self.supplies = ["vdd", "gnd"]
# Used for names/dimensions only
self.cell = factory.create(module_type=OPTS.bitcell)
# Main array
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array)
self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies)
# Replica columns
self.replica_col_insts = []
for port in self.all_ports:
if port in self.rbls:
self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port),
mod=self.replica_columns[port]))
self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies)
else:
self.replica_col_insts.append(None)
# Dummy rows under the bitcell array (connected with with the replica cell wl)
self.dummy_row_replica_insts = []
# Note, this is the number of left and right even if we aren't adding the columns to this bitcell array!
for port in self.all_ports:
self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port),
mod=self.dummy_row))
self.connect_inst(self.all_bitline_names + [x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies)
# Top/bottom dummy rows or col caps
self.dummy_row_insts = []
self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot",
mod=self.col_cap_bottom))
self.connect_inst(self.all_bitline_names + ["gnd"] * len(self.col_cap_bottom.get_wordline_names()) + self.supplies)
self.dummy_row_insts.append(self.add_inst(name="dummy_row_top",
mod=self.col_cap_top))
self.connect_inst(self.all_bitline_names + ["gnd"] * len(self.col_cap_top.get_wordline_names()) + self.supplies)
# Left/right Dummy columns
self.dummy_col_insts = []
self.dummy_col_insts.append(self.add_inst(name="dummy_col_left",
mod=self.row_cap_left))
self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + self.replica_array_wordline_names + self.supplies)
self.dummy_col_insts.append(self.add_inst(name="dummy_col_right",
mod=self.row_cap_right))
self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + self.replica_array_wordline_names + self.supplies)
def create_layout(self):
# This creates space for the unused wordline connections as well as the
# row-based or column based power and ground lines.
self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0]))
self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2]))
self.unused_offset = vector(0.25, 0.25)
# This is a bitcell x bitcell offset to scale
self.bitcell_offset = vector(self.cell.width, self.cell.height)
self.col_end_offset = vector(self.cell.width, self.cell.height)
self.row_end_offset = vector(self.cell.width, self.cell.height)
# Everything is computed with the main array
self.bitcell_array_inst.place(offset=self.unused_offset)
self.add_replica_columns()
self.add_end_caps()
# Array was at (0, 0) but move everything so it is at the lower left
# We move DOWN the number of left RBL even if we didn't add the column to this bitcell array
# Note that this doesn't include the row/col cap
array_offset = self.bitcell_offset.scale(1 + len(self.left_rbl), 1 + self.rbl[0])
self.translate_all(array_offset.scale(-1, -1))
# Add extra width on the left and right for the unused WLs
self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x
self.height = self.dummy_row_insts[1].uy()
self.add_layout_pins()
self.route_supplies()
self.route_unused_wordlines()
lower_left = self.find_lowest_coords()
upper_right = self.find_highest_coords()
self.width = upper_right.x - lower_left.x
self.height = upper_right.y - lower_left.y
self.translate_all(lower_left)
self.add_boundary()
self.DRC_LVS()
def get_main_array_top(self):
""" Return the top of the main bitcell array. """
return self.bitcell_array_inst.uy()
def get_main_array_bottom(self):
""" Return the bottom of the main bitcell array. """
return self.bitcell_array_inst.by()
def get_main_array_left(self):
""" Return the left of the main bitcell array. """
return self.bitcell_array_inst.lx()
def get_main_array_right(self):
""" Return the right of the main bitcell array. """
return self.bitcell_array_inst.rx()
def get_replica_top(self):
""" Return the top of all replica columns. """
return self.dummy_row_insts[0].by()
def get_replica_bottom(self):
""" Return the bottom of all replica columns. """
return self.dummy_row_insts[0].uy()
def get_replica_left(self):
""" Return the left of all replica columns. """
return self.dummy_col_insts[0].rx()
def get_replica_right(self):
""" Return the right of all replica columns. """
return self.dummy_col_insts[1].rx()
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
"""
offsets = [x + self.bitcell_array_inst.lx() for x in self.bitcell_array.get_column_offsets()]
return offsets
def add_replica_columns(self):
""" Add replica columns on left and right of array """
# Grow from left to right, toward the array
for bit, port in enumerate(self.left_rbl):
offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.unused_offset
self.replica_col_insts[bit].place(offset)
# Grow to the right of the bitcell array, array outward
for bit, port in enumerate(self.right_rbl):
offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1)
self.replica_col_insts[self.rbl[0] + bit].place(offset)
# Replica dummy rows
# Add the dummy rows even if we aren't adding the replica column to this bitcell array
# These grow up, toward the array
for bit in range(self.rbl[0]):
dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset
self.dummy_row_replica_insts[bit].place(offset=dummy_offset,
mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0")
# These grow up, away from the array
for bit in range(self.rbl[1]):
dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul()
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset,
mirror="MX" if (self.row_size + bit) % 2 else "R0")
def add_end_caps(self):
""" Add dummy cells or end caps around the array """
# Far top dummy row (first row above array is NOT flipped if even number of rows)
flip_dummy = (self.row_size + self.rbl[1]) % 2
dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul()
self.dummy_row_insts[1].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.rbl[0] + 1) % 2
dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset
self.dummy_row_insts[0].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# Far left dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + self.unused_offset
self.dummy_col_insts[0].place(offset=dummy_col_offset)
# Far right dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl), -self.rbl[0] - 1) + self.bitcell_array_inst.lr()
self.dummy_col_insts[1].place(offset=dummy_col_offset)
def add_layout_pins(self):
""" Add the layout pins """
# All wordlines
# Main array wl and bl/br
for pin_name in self.all_wordline_names:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(0, 1),
width=self.width,
height=pin.height())
# Replica wordlines (go by the row instead of replica column because we may have to add a pin
# even though the column is in another local bitcell array)
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
if wl_name in self.gnd_wordline_names:
continue
pin = inst.get_pin(pin_name)
self.add_layout_pin(text=wl_name,
layer=pin.layer,
offset=pin.ll().scale(0, 1),
width=self.width,
height=pin.height())
for pin_name in self.all_bitline_names:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=self.height)
# Replica bitlines
if len(self.rbls) > 0:
for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts):
pin_names = self.replica_columns[self.rbls[0]].all_bitline_names
for (bl_name, pin_name) in zip(names, pin_names):
pin = inst.get_pin(pin_name)
self.add_layout_pin(text=bl_name,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=self.height)
def route_supplies(self):
if OPTS.bitcell == "pbitcell":
bitcell = factory.create(module_type="pbitcell")
else:
bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports))
vdd_dir = bitcell.vdd_dir
gnd_dir = bitcell.gnd_dir
# vdd/gnd are only connected in the perimeter cells
supply_insts = self.dummy_col_insts + self.dummy_row_insts
# For the wordlines
top_bot_mult = 1
left_right_mult = 1
# There are always vertical pins for the WLs on the left/right if we have unused wordlines
self.left_gnd_locs = self.route_side_pin("gnd", "left", left_right_mult)
self.right_gnd_locs = self.route_side_pin("gnd","right", left_right_mult)
# This needs to be big enough so that they aren't in the same supply routing grid
left_right_mult = 4
if gnd_dir == "V":
self.top_gnd_locs = self.route_side_pin("gnd", "top", top_bot_mult)
self.bot_gnd_locs = self.route_side_pin("gnd", "bot", top_bot_mult)
# This needs to be big enough so that they aren't in the same supply routing grid
top_bot_mult = 4
if vdd_dir == "V":
self.top_vdd_locs = self.route_side_pin("vdd", "top", top_bot_mult)
self.bot_vdd_locs = self.route_side_pin("vdd", "bot", top_bot_mult)
elif vdd_dir == "H":
self.left_vdd_locs = self.route_side_pin("vdd", "left", left_right_mult)
self.right_vdd_locs = self.route_side_pin("vdd", "right", left_right_mult)
else:
debug.error("Invalid vdd direction {}".format(vdd_dir), -1)
for inst in supply_insts:
for pin in inst.get_pins("vdd"):
if vdd_dir == "V":
self.connect_side_pin(pin, "top", self.top_vdd_locs[0].y)
self.connect_side_pin(pin, "bot", self.bot_vdd_locs[0].y)
elif vdd_dir == "H":
self.connect_side_pin(pin, "left", self.left_vdd_locs[0].x)
self.connect_side_pin(pin, "right", self.right_vdd_locs[0].x)
for inst in supply_insts:
for pin in inst.get_pins("gnd"):
if gnd_dir == "V":
self.connect_side_pin(pin, "top", self.top_gnd_locs[0].y)
self.connect_side_pin(pin, "bot", self.bot_gnd_locs[0].y)
elif gnd_dir == "H":
self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """
# This grounds all the dummy row word lines
for inst in self.dummy_row_insts:
for wl_name in self.col_cap_top.get_wordline_names():
pin = inst.get_pin(wl_name)
self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
# Ground the unused replica wordlines
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
if wl_name in self.gnd_wordline_names:
pin = inst.get_pin(pin_name)
self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
def route_side_pin(self, name, side, offset_multiple=1):
"""
Routes a vertical or horizontal pin on the side of the bbox.
The multiple specifies how many track offsets to be away from the side assuming
(0,0) (self.width, self.height)
"""
if side in ["left", "right"]:
return self.route_vertical_side_pin(name, side, offset_multiple)
elif side in ["top", "bottom", "bot"]:
return self.route_horizontal_side_pin(name, side, offset_multiple)
else:
debug.error("Invalid side {}".format(side), -1)
def route_vertical_side_pin(self, name, side, offset_multiple=1):
"""
Routes a vertical pin on the side of the bbox.
"""
if side == "left":
bot_loc = vector(-offset_multiple * self.vertical_pitch, 0)
top_loc = vector(-offset_multiple * self.vertical_pitch, self.height)
elif side == "right":
bot_loc = vector(self.width + offset_multiple * self.vertical_pitch, 0)
top_loc = vector(self.width + offset_multiple * self.vertical_pitch, self.height)
layer = self.supply_stack[2]
top_via = contact(layer_stack=self.supply_stack,
directions=("H", "H"))
# self.add_layout_pin_rect_ends(text=name,
# layer=layer,
# start=bot_loc,
# end=top_loc)
self.add_layout_pin_segment_center(text=name,
layer=layer,
start=bot_loc,
end=top_loc,
width=top_via.second_layer_width)
return (bot_loc, top_loc)
def route_horizontal_side_pin(self, name, side, offset_multiple=1):
"""
Routes a horizontal pin on the side of the bbox.
"""
if side in ["bottom", "bot"]:
left_loc = vector(0, -offset_multiple * self.horizontal_pitch)
right_loc = vector(self.width, -offset_multiple * self.horizontal_pitch)
elif side == "top":
left_loc = vector(0, self.height + offset_multiple * self.horizontal_pitch)
right_loc = vector(self.width, self.height + offset_multiple * self.horizontal_pitch)
layer = self.supply_stack[0]
side_via = contact(layer_stack=self.supply_stack,
directions=("V", "V"))
# self.add_layout_pin_rect_ends(text=name,
# layer=layer,
# start=left_loc,
# end=right_loc)
self.add_layout_pin_segment_center(text=name,
layer=layer,
start=left_loc,
end=right_loc,
width=side_via.first_layer_height)
return (left_loc, right_loc)
def connect_side_pin(self, pin, side, offset):
"""
Used to connect horizontal layers of pins to the left/right straps
locs provides the offsets of the pin strip end points.
"""
if side in ["left", "right"]:
self.connect_vertical_side_pin(pin, side, offset)
elif side in ["top", "bottom", "bot"]:
self.connect_horizontal_side_pin(pin, side, offset)
else:
debug.error("Invalid side {}".format(side), -1)
def connect_horizontal_side_pin(self, pin, side, yoffset):
"""
Used to connect vertical layers of pins to the top/bottom horizontal straps
"""
cell_loc = pin.center()
pin_loc = vector(cell_loc.x, yoffset)
# Place the pins a track outside of the array
self.add_via_stack_center(offset=pin_loc,
from_layer=pin.layer,
to_layer=self.supply_stack[0],
directions=("V", "V"))
# Add a path to connect to the array
self.add_path(pin.layer, [cell_loc, pin_loc])
def connect_vertical_side_pin(self, pin, side, xoffset):
"""
Used to connect vertical layers of pins to the top/bottom vertical straps
"""
cell_loc = pin.center()
pin_loc = vector(xoffset, cell_loc.y)
# Place the pins a track outside of the array
self.add_via_stack_center(offset=pin_loc,
from_layer=pin.layer,
to_layer=self.supply_stack[2],
directions=("H", "H"))
# Add a path to connect to the array
self.add_path(pin.layer, [cell_loc, pin_loc])
def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW."""
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
# Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(corner, load)
# Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
cell_power.leakage * self.column_size * self.row_size)
return total_power
def gen_bl_wire(self):
if OPTS.netlist_only:
height = 0
else:
height = self.height
bl_pos = 0
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1"))
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
return bl_wire
def graph_exclude_bits(self, targ_row=None, targ_col=None):
"""
Excludes bits in column from being added to graph except target
"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def graph_exclude_replica_col_bits(self):
"""
Exclude all replica/dummy cells in the replica columns except the replica bit.
"""
for port in self.left_rbl + self.right_rbl:
self.replica_columns[port].exclude_all_but_replica()
def get_cell_name(self, inst_name, row, col):
"""
Gets the spice name of the target bitcell.
"""
return self.bitcell_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.bitcell_array_inst.name, row, col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bitcell_array.init_graph_params()
+1 -1
View File
@@ -6,7 +6,7 @@
# #
from openram import debug from openram import debug
from openram.modules import bitcell_base from openram.modules.bitcell_base import bitcell_base
from openram.tech import cell_properties as props from openram.tech import cell_properties as props
@@ -6,11 +6,13 @@
# #
from openram import debug from openram import debug
from openram.modules import bitcell_array from openram.modules.bitcell_array import bitcell_array
from openram.modules import pattern
from openram.sram_factory import factory from openram.sram_factory import factory
from openram.base import geometry
from openram import OPTS from openram import OPTS
from .sky130_bitcell_base_array import sky130_bitcell_base_array from .sky130_bitcell_base_array import sky130_bitcell_base_array
from math import ceil
class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array):
""" """
@@ -18,76 +20,37 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array):
Assumes bit-lines and word lines are connected by abutment. Assumes bit-lines and word lines are connected by abutment.
""" """
def __init__(self, rows, cols, column_offset=0, name=""): def __init__(self, rows, cols, column_offset=0, name=""):
# Don't call the regular bitcell_array constructor since we don't want its constructor, just super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
# some of it's useful member functions
sky130_bitcell_base_array.__init__(self, rows=rows, cols=cols, column_offset=column_offset, name=name)
if self.row_size % 2 == 0:
debug.error("Invalid number of rows {}. number of rows (excluding dummy rows) must be odd to connect to col ends".format(self.row_size), -1)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, self.row_size, self.column_size))
self.add_comment("rows: {0} cols: {1}".format(self.row_size, self.column_size))
# This will create a default set of bitline/wordline names
self.create_all_bitline_names()
self.create_all_wordline_names()
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
self.add_supply_pins()
def add_modules(self): def add_modules(self):
""" Add the modules used in this design """ """ Add the modules used in this design """
# Bitcell for port names only # Bitcell for port names only
self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1")
self.cell2 = factory.create(module_type=OPTS.bitcell, version="opt1a") self.cella = factory.create(module_type=OPTS.bitcell, version="opt1a")
self.strap = factory.create(module_type="internal", version="wlstrap") self.strap = factory.create(module_type="internal", version="wlstrap")
self.strap2 = factory.create(module_type="internal", version="wlstrap_p") self.strap_p = factory.create(module_type="internal", version="wlstrap_p")
self.strap3 = factory.create(module_type="internal", version="wlstrapa") self.strapa = factory.create(module_type="internal", version="wlstrapa")
self.strap4 = factory.create(module_type="internal", version="wlstrapa_p") self.strapa_p = factory.create(module_type="internal", version="wlstrapa_p")
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ """ Create the module instances used in this design """
self.cell_inst = {} self.all_inst={}
self.array_layout = [] self.cell_inst={}
alternate_bitcell = (self.row_size) % 2
for row in range(0, self.row_size):
row_layout = [] bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True, mirror='XY')] \
+ [geometry.instance("01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\
+ [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True, mirror='MX')] \
+ [geometry.instance("03_strap", mod=self.strap, is_bitcell=False, mirror='MX')]
bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True, mirror='MY')] \
+ [geometry.instance("11_strap_p", mod=self.strap_p, is_bitcell=False)] \
+ [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True)] \
+ [geometry.instance("13_strapa", mod=self.strapa, is_bitcell=False)]
alternate_strap = (self.row_size+1) % 2 bit_block = []
for col in range(0, self.column_size): pattern.append_row_to_block(bit_block, bit_row_opt1)
if alternate_bitcell == 1: pattern.append_row_to_block(bit_block, bit_row_opt1a)
row_layout.append(self.cell) for row in bit_block:
self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), row = pattern.rotate_list(row, self.column_offset * 2)
mod=self.cell) self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="bit_r{0}_c{1}")
else: self.pattern.connect_array()
row_layout.append(self.cell2)
self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col),
mod=self.cell2)
self.connect_inst(self.get_bitcell_pins(row, col))
if col != self.column_size - 1:
if alternate_strap:
if row % 2:
name="row_{}_col_{}_wlstrapa_p".format(row, col)
row_layout.append(self.strap4)
self.add_inst(name=name, mod=self.strap4)
else:
name="row_{}_col_{}_wlstrap_p".format(row, col)
row_layout.append(self.strap2)
self.add_inst(name=name, mod=self.strap2)
alternate_strap = 0
else:
if row % 2:
name="row_{}_col_{}_wlstrapa".format(row, col)
row_layout.append(self.strap3)
self.add_inst(name=name.format(row, col), mod=self.strap3)
else:
name="row_{}_col_{}_wlstrap".format(row, col)
row_layout.append(self.strap)
self.add_inst(name=name.format(row, col), mod=self.strap)
alternate_strap = 1
self.connect_inst(self.get_strap_pins(row, col, name))
if alternate_bitcell == 0:
alternate_bitcell = 1
else:
alternate_bitcell = 0
self.array_layout.append(row_layout)
@@ -11,7 +11,7 @@ from openram.modules import bitcell_base_array
from openram.sram_factory import factory from openram.sram_factory import factory
from openram.tech import layer from openram.tech import layer
from openram import OPTS from openram import OPTS
from openram.modules import pattern
class sky130_bitcell_base_array(bitcell_base_array): class sky130_bitcell_base_array(bitcell_base_array):
""" """
@@ -23,44 +23,6 @@ class sky130_bitcell_base_array(bitcell_base_array):
self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1")
def place_array(self, name_template, row_offset=0, col_offset=0):
yoffset = 0.0
for row in range(0, len(self.array_layout)):
xoffset = 0.0
for col in range(0, len(self.array_layout[row])):
self.place_inst = self.insts[(col) + (row) * len(self.array_layout[row])]
if row % 2 == 0:
if col == 0:
self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX")
elif col % 4 == 0:
self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX")
elif col % 4 == 3 :
self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX")
elif col % 4 == 2:
self.place_inst.place(offset=[xoffset + self.cell.width, yoffset + self.cell.height], mirror="XY")
else:
self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX")
else:
if col == 0:
self.place_inst.place(offset=[xoffset, yoffset])
elif col % 4 == 0:
self.place_inst.place(offset=[xoffset, yoffset])
elif col % 4 == 3 :
self.place_inst.place(offset=[xoffset, yoffset])
elif col % 4 == 2:
self.place_inst.place(offset=[xoffset + self.cell.width, yoffset], mirror="MY")
# self.place_inst.place(offset=[xoffset, yoffset])
else:
self.place_inst.place(offset=[xoffset, yoffset])
xoffset += self.place_inst.width
yoffset += self.place_inst.height
self.width = max([x.rx() for x in self.insts])
self.height = max([x.uy() for x in self.insts])
def get_bitcell_pins(self, row, col, swap = False): def get_bitcell_pins(self, row, col, swap = False):
""" """
Creates a list of connections in the bitcell, Creates a list of connections in the bitcell,
@@ -122,66 +84,36 @@ class sky130_bitcell_base_array(bitcell_base_array):
strap_pins = ["vdd", "gnd", "vdd"] strap_pins = ["vdd", "gnd", "vdd"]
return strap_pins return strap_pins
def add_supply_pins(self): def route_supplies(self):
""" Add the layout pins """
# Copy a vdd/gnd layout pin from every cell # Copy a vdd/gnd layout pin from every cell
super().route_supplies()
for inst in self.insts: for inst in self.insts:
if "wlstrap" in inst.name: if "VPWR" in inst.mod.pins:
if "VPWR" in inst.mod.pins: self.copy_layout_pin(inst, "VPWR", "vdd")
self.copy_layout_pin(inst, "VPWR", "vdd") if "VGND" in inst.mod.pins:
if "VGND" in inst.mod.pins: self.copy_layout_pin(inst, "VGND", "gnd")
self.copy_layout_pin(inst, "VGND", "gnd")
for row in range(self.row_size): if hasattr(self, 'cell_inst'):
for col in range(self.column_size): for col in range(self.column_size):
inst = self.cell_inst[row, col] inst = self.cell_inst[0,col]
for pin_name in ["vdd", "gnd"]: pin = inst.get_pin("vpb")
self.copy_layout_pin(inst, pin_name) self.objs.append(geometry.rectangle(layer["nwell"],
pin.ll(),
pin.width(),
pin.height()))
self.objs.append(geometry.label("vdd", layer["nwell"], pin.center()))
try:
from openram.tech import layer_override
if layer_override['VNB']:
pin = inst.get_pin("vnb")
self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center()))
self.objs.append(geometry.rectangle(layer["pwellp"],
pin.ll(),
pin.width(),
pin.height()))
except:
pin = inst.get_pin("vnb")
self.add_label("vdd", pin.layer, pin.center())
if row == 2: #add only 1 label per col
if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins:
pin = inst.get_pin("vpb")
self.objs.append(geometry.rectangle(layer["nwell"],
pin.ll(),
pin.width(),
pin.height()))
self.objs.append(geometry.label("vdd", layer["nwell"], pin.center()))
if 'VNB' or 'vnb'in self.cell_inst[row, col].mod.pins:
try:
from openram.tech import layer_override
if layer_override['VNB']:
pin = inst.get_pin("vnb")
self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center()))
self.objs.append(geometry.rectangle(layer["pwellp"],
pin.ll(),
pin.width(),
pin.height()))
except:
pin = inst.get_pin("vnb")
self.add_label("vdd", pin.layer, pin.center())
def add_bitline_pins(self):
bitline_names = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for port in self.all_ports:
bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port])
text = "bl_{0}_{1}".format(port, col)
#if "Y" in self.cell_inst[0, col].mirror:
# text = text.replace("bl", "br")
self.add_layout_pin(text=text,
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1])
text = "br_{0}_{1}".format(port, col)
#if "Y" in self.cell_inst[0, col].mirror:
# text = text.replace("br", "bl")
self.add_layout_pin(text=text,
layer=br_pin.layer,
offset=br_pin.ll().scale(1, 0),
width=br_pin.width(),
height=self.height)
@@ -7,213 +7,194 @@
from openram import debug from openram import debug
from openram.base import vector from openram.base import vector
from openram.base import contact from openram.base import contact
from openram import debug
from openram.base import round_to_grid
from openram.sram_factory import factory from openram.sram_factory import factory
from openram.tech import drc, spice from openram.tech import drc, spice
from openram.tech import cell_properties as props from openram.tech import cell_properties as props
from openram import OPTS from openram import OPTS
from openram.modules.capped_replica_bitcell_array import capped_replica_bitcell_array
from .sky130_bitcell_base_array import sky130_bitcell_base_array from .sky130_bitcell_base_array import sky130_bitcell_base_array
from math import sqrt
class sky130_capped_replica_bitcell_array(sky130_bitcell_base_array): class sky130_capped_replica_bitcell_array(capped_replica_bitcell_array, sky130_bitcell_base_array):
""" """
Creates a replica bitcell array then adds the row and column caps to all Creates a replica bitcell array then adds the row and column caps to all
sides of a bitcell array. sides of a bitcell array.
""" """
def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""):
super().__init__(name, rows, cols, column_offset=0) super().__init__(rows, cols, rbl, left_rbl, right_rbl, name)
debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name,
rows, def add_layout_pins(self):
cols, """ Add the layout pins """
rbl,
left_rbl,
right_rbl))
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.add_comment("rbl: {0} left_rbl: {1} right_rbl: {2}".format(rbl, left_rbl, right_rbl))
# This is how many RBLs are in all the arrays for row_end in self.dummy_col_insts:
self.rbl = rbl row_end = row_end.mod
# This specifies which RBL to put on the left or right by port number for (rba_wl_name, wl_name) in zip(self.get_all_wordline_names(), row_end.get_wordline_names()):
# This could be an empty list pin = row_end.get_pin(wl_name)
if left_rbl is not None: self.add_layout_pin(text=rba_wl_name,
self.left_rbl = left_rbl layer=pin.layer,
else: offset=vector(0,pin.ll().scale(0, 1)[1]),
self.left_rbl = [] #width=self.width,
# This could be an empty list width=pin.width(),
if right_rbl is not None: height=pin.height())
self.right_rbl = right_rbl
else:
self.right_rbl = []
self.create_netlist() pin_height = (round_to_grid(drc["minarea_m3"] / round_to_grid(sqrt(drc["minarea_m3"]))) + drc["{0}_to_{0}".format('m3')])
if not OPTS.netlist_only: drc_width = drc["{0}_to_{0}".format('m3')]
self.create_layout()
def create_netlist(self): # vdd/gnd are only connected in the perimeter cells
""" Create and connect the netlist """ # replica column should only have a vdd/gnd in the dummy cell on top/bottom
self.add_modules() supply_insts = self.dummy_row_insts
self.add_pins()
self.create_instances()
def add_modules(self): for pin_name in self.supplies:
self.replica_bitcell_array = factory.create(module_type="replica_bitcell_array", for supply_inst in supply_insts:
cols=self.column_size, vdd_alternate = 0
rows=self.row_size, gnd_alternate = 0
rbl=self.rbl, for cell_inst in supply_inst.mod.insts:
left_rbl=self.left_rbl, inst = cell_inst.mod
right_rbl=self.right_rbl) for pin in inst.get_pins(pin_name):
if pin.name == 'vdd':
def add_pins(self): if vdd_alternate:
connection_offset = -0.02
# Arrays are always: vdd_alternate = 0
# bitlines (column first then port order) else:
# word lines (row first then port order) connection_offset = 0.02
# dummy wordlines vdd_alternate = 1
# replica wordlines connection_width = drc["minwidth_{}".format('m1')]
# regular wordlines (bottom to top) track_offset = 1
# # dummy bitlines elif pin.name == 'gnd':
# replica bitlines (port order) if gnd_alternate:
# regular bitlines (left to right port order) connection_offset = 0.00
# gnd_alternate = 0
# vdd else:
# gnd connection_offset = 0.00
gnd_alternate = 1
self.add_bitline_pins() connection_width = drc["minwidth_{}".format('m1')]
self.add_wordline_pins() track_offset = 4
self.add_pin("vdd", "POWER") pin_width = round_to_grid(sqrt(drc["minarea_m3"]))
self.add_pin("gnd", "GROUND") pin_height = round_to_grid(drc["minarea_m3"] / pin_width)
if inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_cent' or 'corner' in inst.cell_name:
def add_bitline_pins(self): if 'dummy_row_bot' in supply_inst.name:
self.bitline_names = self.replica_bitcell_array.bitline_names pin_center = vector(pin.center()[0], -1 * track_offset * (pin_height + drc_width*2))
self.all_bitline_names = self.replica_bitcell_array.all_bitline_names self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width)
self.rbl_bitline_names = self.replica_bitcell_array.rbl_bitline_names elif 'dummy_row_top' in supply_inst.name:
self.all_rbl_bitline_names = self.replica_bitcell_array.all_rbl_bitline_names pin_center = vector(pin.center()[0],inst.height + 1 * track_offset* (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, self.height), connection_width)
self.bitline_pins = [] # elif 'replica_col' in supply_inst.name and cell_inst.mirror == 'MX':
# pin_center = vector(pin.center()[0], -1 * track_offset* (pin_height + drc_width*2))
for port in self.left_rbl: # self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width)
self.bitline_pins.extend(self.rbl_bitline_names[port]) # elif 'replica_col' in supply_inst.name:
self.bitline_pins.extend(self.all_bitline_names) # pin_center = vector(pin.center()[0],inst.height + 1 * track_offset * (pin_height + drc_width*2))
for port in self.right_rbl: # self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset,self.height), connection_width)
self.bitline_pins.extend(self.rbl_bitline_names[port]) self.add_via_stack_center(from_layer=pin.layer,
to_layer='m2',
self.add_pin_list(self.bitline_pins, "INOUT") offset=pin_center+supply_inst.ll()+cell_inst.ll() + vector(connection_offset,0))
def add_wordline_pins(self):
# some of these are just included for compatibility with modules instantiating this module
self.rbl_wordline_names = self.replica_bitcell_array.rbl_wordline_names
self.all_rbl_wordline_names = self.replica_bitcell_array.all_rbl_wordline_names
self.wordline_names = self.replica_bitcell_array.wordline_names
self.all_wordline_names = self.replica_bitcell_array.all_wordline_names
self.wordline_pins = []
for port in range(self.rbl[0]):
self.wordline_pins.append(self.rbl_wordline_names[port][port])
self.wordline_pins.extend(self.all_wordline_names)
for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]):
self.wordline_pins.append(self.rbl_wordline_names[port][port])
self.add_pin_list(self.wordline_pins, "INPUT")
def create_instances(self):
""" Create the module instances used in this design """
self.supplies = ["vdd", "gnd"]
# Main array
self.replica_bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
mod=self.replica_bitcell_array)
self.connect_inst(self.bitline_pins + self.wordline_pins + self.supplies)
def create_layout(self):
self.replica_bitcell_array_inst.place(offset=0)
self.width = self.replica_bitcell_array.width
self.height = self.replica_bitcell_array.height
for pin_name in self.bitline_pins + self.wordline_pins + self.supplies:
self.copy_layout_pin(self.replica_bitcell_array_inst, pin_name)
self.add_boundary()
self.DRC_LVS()
def get_main_array_top(self):
return self.replica_bitcell_array.get_main_array_top()
def get_main_array_bottom(self):
return self.replica_bitcell_array.get_main_array_bottom()
def get_main_array_left(self):
return self.replica_bitcell_array.get_main_array_left()
def get_main_array_right(self):
return self.replica_bitcell_array.get_main_array_right()
def get_replica_top(self):
return self.replica_bitcell_array.get_replica_top()
def get_replica_bottom(self):
return self.replica_bitcell_array.get_replica_bottom()
def get_replica_left(self):
return self.replica_bitcell_array.get_replica_left()
def get_replica_right(self):
return self.replica_bitcell_array.get_replica_right()
def get_column_offsets(self): # add well contacts to perimeter cells
return self.replica_bitcell_array.get_column_offsets() for pin_name in ['vpb', 'vnb']:
for supply_inst in supply_insts:
vnb_alternate = 0
vpb_alternate = 0
for cell_inst in supply_inst.mod.insts:
def analytical_power(self, corner, load): inst = cell_inst.mod
"""Power of Bitcell array and bitline in nW.""" for pin in inst.get_pins(pin_name):
# Dynamic Power from Bitline if pin.name == 'vpb':
bl_wire = self.gen_bl_wire() if vpb_alternate:
cell_load = 2 * bl_wire.return_input_cap() connection_offset = 0.01
bl_swing = OPTS.rbl_delay_percentage vpb_alternate = 0
freq = spice["default_event_frequency"] else:
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) connection_offset = 0.02
vpb_alternate = 1
connection_width = drc["minwidth_{}".format('m1')]
track_offset = 2
elif pin.name == 'vnb':
if vnb_alternate:
connection_offset = -0.01
vnb_alternate = 0
else:
connection_offset = -0.02
vnb_alternate = 1
connection_width = drc["minwidth_{}".format('m1')]
track_offset = 3
if inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_cent':
if 'dummy_row_bot' in supply_inst.name:
pin_center = vector(pin.center()[0], -1 * track_offset * (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width)
elif 'dummy_row_top' in supply_inst.name:
pin_center = vector(pin.center()[0],inst.height + 1 * track_offset* (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, self.height), connection_width)
# elif 'replica_col' in supply_inst.name:
# pin_center = vector(pin.center()[0], -1 * track_offset* (pin_height + drc_width*2))
# self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width)
# elif 'replica_col' in supply_inst.name:
# pin_center = vector(pin.center()[0],inst.height + 1 * track_offset * (pin_height + drc_width*2))
# self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset,self.height), connection_width)
self.add_via_stack_center(from_layer=pin.layer,
to_layer='m2',
offset=pin_center+supply_inst.ll()+cell_inst.ll() + vector(connection_offset,0))
# Calculate the bitcell power which currently only includes leakage min_area = drc["minarea_{}".format('m3')]
cell_power = self.cell.analytical_power(corner, load) for track,supply, offset in zip(range(1,5),['vdd','vdd','gnd','gnd'],[min_area * 6,min_area * 6, 0, 0]):
y_offset = track * (pin_height + drc_width*2)
self.add_segment_center('m2', vector(-0.4,-y_offset), vector(self.width+0.4, -y_offset), drc["minwidth_{}".format('m2')])
self.add_segment_center('m2', vector(-0.4,self.height + y_offset), vector(self.width+0.4, self.height + y_offset), drc["minwidth_{}".format('m2')])
self.add_power_pin(name=supply,
loc=vector(round_to_grid(sqrt(min_area))/2 + offset, -y_offset),
start_layer='m2')
self.add_power_pin(name=supply,
loc=vector(round_to_grid(sqrt(min_area))/2 + offset, self.height + y_offset),
start_layer='m2')
self.add_power_pin(name=supply,
loc=vector(self.width - round_to_grid(sqrt(min_area))/2 - offset, -y_offset),
start_layer='m2')
self.add_power_pin(name=supply,
loc=vector(self.width - round_to_grid(sqrt(min_area))/2 - offset, self.height + y_offset),
start_layer='m2')
# Leakage power grows with entire array and bitlines. self.offset_all_coordinates()
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, self.height = self.height + self.dummy_col_insts[0].lr().y * 2
cell_power.leakage * self.column_size * self.row_size)
return total_power
for pin_name in self.bitline_pin_list:
def gen_bl_wire(self): pin_list = self.replica_bitcell_array_inst.get_pins(pin_name)
if OPTS.netlist_only: for pin in pin_list:
height = 0 if 'bl' in pin.name:
else: self.add_layout_pin(text=pin_name,
height = self.height layer=pin.layer,
bl_pos = 0 offset=pin.ll().scale(1, 0),
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) width=pin.width(),
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell height=self.height)
return bl_wire elif 'br' in pin_name:
self.add_layout_pin(text=pin_name,
def graph_exclude_bits(self, targ_row=None, targ_col=None): layer=pin.layer,
""" offset=pin.ll().scale(1, 0) + vector(0,pin_height + drc_width*2),
Excludes bits in column from being added to graph except target width=pin.width(),
""" height=self.height - 2 *(pin_height + drc_width*2))
self.replica_bitcell_array.graph_exclude_bits(targ_row, targ_col)
# # Replica bitlines
def graph_exclude_replica_col_bits(self): # if len(self.rbls) > 0:
""" # for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts):
Exclude all replica/dummy cells in the replica columns except the replica bit. # pin_names = self.replica_bitcell_array_inst.mod.replica_columns[self.rbls[0]].all_bitline_names
""" # mirror = self.replica_col_insts[0].mirror
self.replica_bitcell_array.graph_exclude_replica_col_bits() # for (bl_name, pin_name) in zip(names, pin_names):
# pin = inst.get_pin(pin_name)
def get_cell_name(self, inst_name, row, col): # if 'rbl_bl' in bl_name:
""" # # if mirror != "MY":
Gets the spice name of the target bitcell. # # bl_name = bl_name.replace("rbl_bl","rbl_br")
""" # self.add_layout_pin(text=bl_name,
return self.replica_bitcell_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.replica_bitcell_array_inst.name, row, col) # layer=pin.layer,
# offset=pin.ll().scale(1, 0),
def clear_exclude_bits(self): # width=pin.width(),
""" # height=self.height)
Clears the bit exclusions # elif 'rbl_br' in bl_name:
""" # # if mirror != "MY":
self.replica_bitcell_array.clear_exclude_bits() # # bl_name = bl_name.replace("rbl_br","rbl_bl")
# self.add_layout_pin(text=bl_name,
# layer=pin.layer,
# offset=pin.ll().scale(1, 0) + vector(0,(pin_height + drc_width*2)),
# width=pin.width(),
# height=self.height - 2 *(pin_height + drc_width*2))
return
+60 -220
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# See LICENSE for licensing information. # See LICENSE for licensing information.
# #
# Copyright (c) 2016-2023 Regents of the University of California # Copyright (c) 2016-2023 Regents of the University of California
@@ -9,42 +9,18 @@ from openram.base import geometry
from openram.sram_factory import factory from openram.sram_factory import factory
from openram.tech import layer from openram.tech import layer
from openram import OPTS from openram import OPTS
from openram.modules.col_cap_array import col_cap_array
from .sky130_bitcell_base_array import sky130_bitcell_base_array from .sky130_bitcell_base_array import sky130_bitcell_base_array
from openram.modules import pattern
from math import ceil
class sky130_col_cap_array(sky130_bitcell_base_array): class sky130_col_cap_array(col_cap_array, sky130_bitcell_base_array):
""" """
Generate a dummy row/column for the replica array. Generate a dummy row/column for the replica array.
""" """
def __init__(self, rows, cols, location, column_offset=0, mirror=0, name=""): def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""):
# Don't call the regular col-cap_array constructor since we don't want its constructor, just super().__init__(rows, cols, column_offset=column_offset, mirror=mirror, location=location, name=name)
# some of it's useful member functions self.no_instances = False
sky130_bitcell_base_array.__init__(self, rows=rows, cols=cols, column_offset=column_offset, name=name)
self.mirror = mirror
self.location = location
self.rows = rows
self.cols = cols
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
# This module has no wordlines
# self.create_all_wordline_names()
# This module has no bitlines
# self.create_all_bitline_names()
self.add_modules()
self.create_all_wordline_names()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place_array("dummy_r{0}_c{1}", self.mirror)
self.add_layout_pins()
self.add_supply_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self): def add_modules(self):
""" Add the modules used in this design """ """ Add the modules used in this design """
@@ -60,196 +36,60 @@ class sky130_col_cap_array(sky130_bitcell_base_array):
self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1")
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ self.all_inst={}
self.cell_inst = {} self.cell_inst={}
self.array_layout = []
bitline = 0 if self.location == "top":
for col in range((self.column_size * 2) - 1): bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True)] \
row_layout = [] + [geometry.instance("01_strap_p_cent", mod=self.colend2, is_bitcell=False)]\
name="rca_{0}_{1}".format(self.location, col) + [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="MY")] \
# Top/bottom cell are always dummy cells. + [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False)]
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl) elif self.location == "bottom":
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell. bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True, mirror="MX")] \
pins = [] + [geometry.instance("01_strap_p_cent", mod=self.colend2, is_bitcell=False, mirror="MX")]\
if col % 4 == 0: + [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="XY")] \
row_layout.append(self.colend1) + [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False, mirror="MX")]
self.cell_inst[col]=self.add_inst(name=name, mod=self.colend1)
pins.append("fake_bl_{}".format(bitline))
pins.append("vdd")
pins.append("gnd")
pins.append("fake_br_{}".format(bitline))
pins.append("gate")
pins.append("vdd")
pins.append("gnd")
bitline += 1
elif col % 4 == 1:
row_layout.append(self.colend2)
self.cell_inst[col]=self.add_inst(name=name, mod=self.colend3)
pins.append("vdd")
pins.append("vdd")
pins.append("gnd")
elif col % 4 == 2:
row_layout.append(self.colend1)
self.cell_inst[col]=self.add_inst(name=name, mod=self.colend1)
pins.append("fake_bl_{}".format(bitline))
pins.append("vdd")
pins.append("gnd")
pins.append("fake_br_{}".format(bitline))
pins.append("gate")
pins.append("vdd")
pins.append("gnd")
bitline += 1
elif col % 4 ==3:
row_layout.append(self.colend2)
self.cell_inst[col]=self.add_inst(name=name, mod=self.colend2)
pins.append("gnd")
pins.append("vdd")
pins.append("vnb")
self.connect_inst(pins) bit_row = pattern.rotate_list(bit_row, self.column_offset * 2)
bit_block = []
pattern.append_row_to_block(bit_block, bit_row)
self.pattern = pattern(self, "col_cap_array_" + self.location , bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="col_cap_array" + self.location + "_r{0}_c{1}")
self.pattern.connect_array()
def get_bitcell_pins(self, row, col):
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = []
for port in self.all_ports:
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))])
bitcell_pins.append("vdd") # vdd
bitcell_pins.append("gnd") # gnd
bitcell_pins.append("vdd") # vpb
bitcell_pins.append("gnd") # vnb
bitcell_pins.append("gnd")# poly gate for parasitic tx
#bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))])
self.array_layout.append(row_layout) return bitcell_pins
def place_array(self, name_template, row_offset=0):
xoffset = 0.0
yoffset = 0.0
for col in range(len(self.insts)):
inst = self.insts[col]
if col % 4 == 0:
inst.place(offset=[xoffset + inst.width, yoffset], mirror="MY")
elif col % 4 == 1:
inst.place(offset=[xoffset, yoffset])
elif col % 4 == 2:
inst.place(offset=[xoffset, yoffset])
elif col % 4 ==3:
inst.place(offset=[xoffset, yoffset])
xoffset += inst.width
self.width = max([x.rx() for x in self.insts])
self.height = max([x.uy() for x in self.insts])
def add_pins(self):
for fake_bl in range(self.cols):
self.add_pin("fake_bl_{}".format(fake_bl), "OUTPUT")
self.add_pin("fake_br_{}".format(fake_bl), "OUTPUT")
#self.add_pin("fake_wl", "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
self.add_pin("gate", "BIAS")
def add_layout_pins(self):
""" Add the layout pins """
# Add vdd/gnd via stacks
for cols in range((self.column_size * 2) - 1):
inst = self.cell_inst[cols]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
if inst.mod.cell_name == 'sky130_fd_bd_sram__sram_sp_colend' or 'sky130_fd_bd_sram__sram_sp_colenda':
if inst.mirror == "MY":
if pin_name == "vdd" and pin.layer == 'm1':
self.add_layout_pin_rect_center(text="vdd",
layer=pin.layer,
offset=inst.lr(),
width=pin.width(),
height=pin.height())
elif pin_name == "gnd" and pin.layer == 'm1':
self.add_layout_pin_rect_center(text="gnd",
layer=pin.layer,
offset=inst.ll(),
width=pin.width(),
height=pin.height())
else:
if pin_name == "vdd" and pin.layer == 'm1':
self.add_layout_pin_rect_center(text="vdd",
layer=pin.layer,
offset=inst.ll(),
width=pin.width(),
height=pin.height())
elif pin_name == "gnd" and pin.layer == 'm1':
self.add_layout_pin_rect_center(text="gnd",
layer=pin.layer,
offset=inst.lr(),
width=pin.width(),
height=pin.height())
for col in range(len(self.insts)):
inst = self.insts[col]
if col % 4 == 0:
pin = self.cell_inst[col].get_pin("bl")
text = "fake_bl_{}".format(int(col/2))
self.add_layout_pin(text=text,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=pin.height())
pin = self.cell_inst[col].get_pin("br")
text = "fake_br_{}".format(int(col/2))
self.add_layout_pin(text=text,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=pin.height())
elif col % 4 == 2:
pin = self.cell_inst[col].get_pin("bl")
text = "fake_bl_{}".format(int(col/2))
self.add_layout_pin(text=text,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=pin.height())
pin = self.cell_inst[col].get_pin("br")
text = "fake_br_{}".format(int(col/2))
self.add_layout_pin(text=text,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=pin.height())
return
def add_supply_pins(self): def get_strap_pins(self, row, col):
for col in range(len(self.insts)):
inst = self.cell_inst[col] strap_pins = []
if col % 2 == 0 and col % 4 != 0:
strap_pins.append("vdd") # vdd
else:
strap_pins.append("gnd") # gnd
strap_pins.append("vdd") # vpb
strap_pins.append("gnd") # vnb
return strap_pins
def create_layout(self):
if 'VPB' or 'vnb' in self.cell_inst[col].mod.pins: self.place_array()
pin = inst.get_pin("vpb") self.add_layout_pins()
self.objs.append(geometry.rectangle(layer["nwell"],
pin.ll(),
pin.width(),
pin.height()))
self.objs.append(geometry.label("vdd", layer["nwell"], pin.center()))
self.add_boundary()
if 'VNB' or 'vnb' in self.cell_inst[col].mod.pins: self.DRC_LVS()
try:
from openram.tech import layer_override
if layer_override['VNB']:
pin = inst.get_pin("vnb")
self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center()))
self.objs.append(geometry.rectangle(layer["pwellp"],
pin.ll(),
pin.width(),
pin.height()))
except:
pin = inst.get_pin("vnb")
self.add_label("vdd", pin.layer, pin.center())
def create_all_wordline_names(self, row_size=None):
if row_size == None:
row_size = self.row_size
for row in range(row_size):
for port in self.all_ports:
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
+8 -13
View File
@@ -7,27 +7,22 @@
from openram import debug from openram import debug
from openram.base import design from openram.base import design
from openram.base import get_libcell_size from openram.tech import cell_properties as props
from openram.tech import layer, GDS
class sky130_corner(design): class sky130_corner(design):
def __init__(self, location, name=""): def __init__(self, location, name=""):
super().__init__(name)
if location == "ul": if location == "ul":
self.name = "sky130_fd_bd_sram__sram_sp_corner" cell_name = "sky130_fd_bd_sram__sram_sp_corner"
elif location == "ur": elif location == "ur":
self.name = "sky130_fd_bd_sram__sram_sp_cornerb" cell_name = "sky130_fd_bd_sram__sram_sp_cornerb"
elif location == "ll": elif location == "ll":
self.name = "sky130_fd_bd_sram__sram_sp_cornera" cell_name = "sky130_fd_bd_sram__sram_sp_cornera"
elif location == "lr": elif location == "lr":
self.name = "sky130_fd_bd_sram__sram_sp_cornera" cell_name = "sky130_fd_bd_sram__sram_sp_cornera"
else: else:
debug.error("Invalid sky130_corner location", -1) debug.error("Invalid sky130_corner location", -1)
design.__init__(self, name=self.name) super().__init__(name=name, cell_name=cell_name, prop=props.col_cap_1port_strap_power)
(self.width, self.height) = get_libcell_size(self.name, self.no_instances = True
GDS["unit"],
layer["mem"])
# pin_map = get_libcell_pins(pin_names, self.name, GDS["unit"])
+38 -164
View File
@@ -10,186 +10,60 @@ from openram.sram_factory import factory
from openram.tech import layer from openram.tech import layer
from openram import OPTS from openram import OPTS
from .sky130_bitcell_base_array import sky130_bitcell_base_array from .sky130_bitcell_base_array import sky130_bitcell_base_array
from openram.modules import dummy_array
from openram.modules import pattern
from math import ceil
class sky130_dummy_array(sky130_bitcell_base_array): class sky130_dummy_array(dummy_array, sky130_bitcell_base_array):
""" """
Generate a dummy row/column for the replica array. Generate a dummy row/column for the replica array.
""" """
def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name=""): def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name=""):
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.mirror = mirror
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
# This will create a default set of bitline/wordline names
self.create_all_bitline_names()
self.create_all_wordline_names()
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place_array("dummy_r{0}_c{1}", self.mirror)
self.add_layout_pins()
self.add_supply_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self): def add_modules(self):
""" Add the modules used in this design """ """ Add the modules used in this design """
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell, version="opt1") self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell, version="opt1")
self.dummy_cell2 = factory.create(module_type=OPTS.dummy_bitcell, version="opt1a") self.dummy_cella = factory.create(module_type=OPTS.dummy_bitcell, version="opt1a")
self.strap = factory.create(module_type="internal", version="wlstrap") self.strap = factory.create(module_type="internal", version="wlstrap")
self.strap2 = factory.create(module_type="internal", version="wlstrap_p") self.strap_p = factory.create(module_type="internal", version="wlstrap_p")
self.strap3 = factory.create(module_type="internal", version="wlstrapa") self.strapa = factory.create(module_type="internal", version="wlstrapa")
self.strap4 = factory.create(module_type="internal", version="wlstrapa_p") self.strapa_p = factory.create(module_type="internal", version="wlstrapa_p")
self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1")
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ """ Create the module instances used in this design """
self.cell_inst = {} # this code needs to use connect_array_raw() to make dummy columns correctly, but single port shouldn't need these since there are dedicated cap cells
self.array_layout = [] self.all_inst={}
alternate_bitcell = (self.row_size + 1) % 2 self.cell_inst={}
for row in range(0, self.row_size):
bit_row_opt1 = [geometry.instance("00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MY')] \
+ [geometry.instance("01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='')]\
+ [geometry.instance("02_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='')] \
+ [geometry.instance("03_strap", mod=self.strap, is_bitcell=False, mirror='')]
bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cella, is_bitcell=True, mirror='MY')] \
+ [geometry.instance("11_strap_p", mod=self.strap_p, is_bitcell=False)] \
+ [geometry.instance("12_opt1a", mod=self.dummy_cella, is_bitcell=True)] \
+ [geometry.instance("13_strapaa", mod=self.strapa, is_bitcell=False)]
row_layout = []
bit_block = []
alternate_strap = (self.row_size + 1) % 2 if(self.row_offset % 2 == 0):
for col in range(0, self.column_size): next_row = 0
if alternate_bitcell == 1: else:
row_layout.append(self.dummy_cell) next_row = 1
self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col),
mod=self.dummy_cell) for i in range(self.row_size):
else: if next_row == 0:
row_layout.append(self.dummy_cell2) pattern.append_row_to_block(bit_block, bit_row_opt1)
self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), next_row = 1
mod=self.dummy_cell2)
self.connect_inst(self.get_bitcell_pins(row, col))
if col != self.column_size - 1:
if alternate_strap:
if col % 2:
name="row_{}_col_{}_wlstrap_p".format(row, col)
row_layout.append(self.strap4)
self.add_inst(name=name,
mod=self.strap4)
else:
name="row_{}_col_{}_wlstrapa_p".format(row, col)
row_layout.append(self.strap2)
self.add_inst(name=name,
mod=self.strap2)
alternate_strap = 0
else:
if col % 2:
name="row_{}_col_{}_wlstrap".format(row, col)
row_layout.append(self.strap)
self.add_inst(name=name,
mod=self.strap)
else:
name="row_{}_col_{}_wlstrapa".format(row, col)
row_layout.append(self.strap3)
self.add_inst(name=name,
mod=self.strap3)
alternate_strap = 1
self.connect_inst(self.get_strap_pins(row, col, name))
if alternate_bitcell == 0:
alternate_bitcell = 1
else: else:
alternate_bitcell = 0 pattern.append_row_to_block(bit_block, bit_row_opt1a)
self.array_layout.append(row_layout) next_row = 0
def add_pins(self): for row in bit_block:
# bitline pins are not added because they are floating row = pattern.rotate_list(row, self.column_offset * 2)
for bl in range(self.column_size):
self.add_pin("bl_0_{}".format(bl))
self.add_pin("br_0_{}".format(bl))
for wl_name in self.get_wordline_names():
self.add_pin(wl_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
#self.add_pin("vpb", "BIAS")
#Sself.add_pin("vnb", "BIAS")
def add_layout_pins(self): self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="dummy_bit_r{0}_c{1}")
""" Add the layout pins """ self.pattern.connect_array()
bitline_names = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for port in self.all_ports:
bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port])
text = "bl_{0}_{1}".format(port, col)
self.add_layout_pin(text=text,
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1])
text = "br_{0}_{1}".format(port, col)
self.add_layout_pin(text=text,
layer=br_pin.layer,
offset=br_pin.ll().scale(1, 0),
width=br_pin.width(),
height=self.height)
# self.add_rect(layer=bl_pin.layer,
# offset=bl_pin.ll().scale(1, 0),
# width=bl_pin.width(),
# height=self.height)
# self.add_rect(layer=br_pin.layer,
# offset=br_pin.ll().scale(1, 0),
# width=br_pin.width(),
# height=self.height)
wl_names = self.cell.get_all_wl_names()
for row in range(self.row_size):
for port in self.all_ports:
wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port])
self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
# Copy a vdd/gnd layout pin from every cell
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
def add_supply_pins(self):
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins:
pin = inst.get_pin("vpb")
self.objs.append(geometry.rectangle(layer["nwell"],
pin.ll(),
pin.width(),
pin.height()))
self.objs.append(geometry.label("vdd", layer["nwell"], pin.center()))
if 'VNB' or 'vnb' in self.cell_inst[row, col].mod.pins:
try:
from openram.tech import layer_override
if layer_override['VNB']:
pin = inst.get_pin("vnb")
self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center()))
self.objs.append(geometry.rectangle(layer["pwellp"],
pin.ll(),
pin.width(),
pin.height()))
except:
pin = inst.get_pin("vnb")
self.add_label("vdd", pin.layer, pin.center())
def input_load(self):
# FIXME: This appears to be old code from previous characterization. Needs to be updated.
wl_wire = self.gen_wl_wire()
return wl_wire.return_input_cap()
@@ -13,7 +13,7 @@ from openram.tech import drc
from openram.tech import array_row_multiple from openram.tech import array_row_multiple
from openram.tech import array_col_multiple from openram.tech import array_col_multiple
from openram import OPTS from openram import OPTS
from .replica_bitcell_array import replica_bitcell_array from openram.modules.replica_bitcell_array import replica_bitcell_array
from .sky130_bitcell_base_array import sky130_bitcell_base_array from .sky130_bitcell_base_array import sky130_bitcell_base_array
@@ -27,389 +27,6 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar
bitcell (Bl/BR disconnected). bitcell (Bl/BR disconnected).
""" """
def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""):
total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports debug.check((cols+ sum(rbl)) % 2==0, "must have an even number of cols including replica cols; you can add a spare col to fix this")
self.all_ports = list(range(total_ports)) super().__init__(rows, cols, rbl, left_rbl, right_rbl, name)
self.column_size = cols
self.row_size = rows
# This is how many RBLs are in all the arrays
if rbl:
self.rbl = rbl
else:
self.rbl=[1, 1 if len(self.all_ports)>1 else 0]
# This specifies which RBL to put on the left or right
# by port number
# This could be an empty list
if left_rbl != None:
self.left_rbl = left_rbl
else:
self.left_rbl = [0]
# This could be an empty list
if right_rbl != None:
self.right_rbl = right_rbl
else:
self.right_rbl=[1] if len(self.all_ports) > 1 else []
self.rbls = self.left_rbl + self.right_rbl
if ((self.column_size + self.rbl[0] + self.rbl[1]) % array_col_multiple != 0):
debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.column_size + self.rbl[0] + self.rbl[1], array_col_multiple), -1)
if ((self.row_size + self.rbl[0] + self.rbl[1]) % array_row_multiple != 0):
debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.row_size + self.rbl[0] + self.rbl[1], array_row_multiple), -15)
super().__init__(self.row_size, self.column_size, rbl, left_rbl, right_rbl, name)
def create_layout(self):
# We will need unused wordlines grounded, so we need to know their layer
# and create a space on the left and right for the vias to connect to ground
pin = self.cell.get_pin(self.cell.get_all_wl_names()[0])
pin_layer = pin.layer
self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
self.unused_offset = vector(self.unused_pitch, 0)
# This is a bitcell x bitcell offset to scale
self.bitcell_offset = vector(self.cell.width, self.cell.height)
self.strap_offset = vector(self.replica_col_insts[0].mod.strap1.width, self.replica_col_insts[0].mod.strap1.height)
self.col_end_offset = vector(self.dummy_row_insts[0].mod.colend1.width, self.dummy_row_insts[0].mod.colend1.height)
self.row_end_offset = vector(self.dummy_col_insts[0].mod.rowend1.width, self.dummy_col_insts[0].mod.rowend1.height)
# Everything is computed with the main array at (self.unused_pitch, 0) to start
self.bitcell_array_inst.place(offset=self.unused_offset)
self.add_replica_columns()
self.add_end_caps()
# Array was at (0, 0) but move everything so it is at the lower left
self.offset_all_coordinates()
# Add extra width on the left and right for the unused WLs
#self.width = self.dummy_col_insts[0].rx() + self.unused_offset[0]
self.width = self.dummy_col_insts[1].rx()
self.height = self.dummy_col_insts[0].uy()
self.add_layout_pins()
self.route_unused_wordlines()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
super().add_pins()
def add_replica_columns(self):
""" Add replica columns on left and right of array """
# Grow from left to right, toward the array
for bit, port in enumerate(self.left_rbl):
offset = self.bitcell_array_inst.ll() \
- vector(0, self.col_cap_bottom.height) \
- vector(0, self.dummy_row.height) \
- vector(self.replica_columns[0].width, 0)
self.replica_col_insts[bit].place(offset + vector(0, self.replica_col_insts[bit].height), mirror="MX")
# Grow to the right of the bitcell array, array outward
for bit, port in enumerate(self.right_rbl):
offset = self.bitcell_array_inst.lr() \
+ self.bitcell_offset.scale(bit, -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) \
+ self.strap_offset.scale(bit, -self.rbl[0] - 1)
self.replica_col_insts[self.rbl[0] + bit].place(offset)
# Replica dummy rows
# Add the dummy rows even if we aren't adding the replica column to this bitcell array
# These grow up, toward the array
for bit in range(self.rbl[0]):
dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset
self.dummy_row_replica_insts[bit].place(offset=dummy_offset,
mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0")
# These grow up, away from the array
for bit in range(self.rbl[1]):
dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul()
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset,
mirror="MX" if bit % 2 else "R0")
def add_end_caps(self):
""" Add dummy cells or end caps around the array """
dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1]) + self.bitcell_array_inst.ul()
self.dummy_row_insts[1].place(offset=dummy_row_offset)
dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) + self.unused_offset
self.dummy_row_insts[0].place(offset=dummy_row_offset + vector(0, self.dummy_row_insts[0].height), mirror="MX")
# Far left dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl) * (1 + self.strap_offset.x / self.cell.width), -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) - vector(self.replica_col_insts[0].width, 0) + self.unused_offset
self.dummy_col_insts[0].place(offset=dummy_col_offset, mirror="MY")
# Far right dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl) * (1 + self.strap_offset.x / self.cell.width), -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) + self.bitcell_array_inst.lr()
self.dummy_col_insts[1].place(offset=dummy_col_offset)
def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """
return
# This grounds all the dummy row word lines
for inst in self.dummy_row_insts:
for wl_name in self.col_cap.get_wordline_names():
self.ground_pin(inst, wl_name)
# Ground the unused replica wordlines
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
if wl_name in self.gnd_wordline_names:
self.ground_pin(inst, pin_name)
def add_layout_pins(self):
""" Add the layout pins """
for row_end in self.dummy_col_insts:
row_end = row_end.mod
for (rba_wl_name, wl_name) in zip(self.get_all_wordline_names(), row_end.get_wordline_names()):
pin = row_end.get_pin(wl_name)
self.add_layout_pin(text=rba_wl_name,
layer=pin.layer,
offset=vector(0,pin.ll().scale(0, 1)[1]),
#width=self.width,
width=pin.width(),
height=pin.height())
pin_height = (round_to_grid(drc["minarea_m3"] / round_to_grid(sqrt(drc["minarea_m3"]))) + drc["{0}_to_{0}".format('m3')])
drc_width = drc["{0}_to_{0}".format('m3')]
# vdd/gnd are only connected in the perimeter cells
# replica column should only have a vdd/gnd in the dummy cell on top/bottom
supply_insts = self.dummy_row_insts + self.replica_col_insts
for pin_name in self.supplies:
for supply_inst in supply_insts:
vdd_alternate = 0
gnd_alternate = 0
for cell_inst in supply_inst.mod.insts:
inst = cell_inst.mod
for pin in inst.get_pins(pin_name):
if pin.name == 'vdd':
if vdd_alternate:
connection_offset = 0.035
vdd_alternate = 0
else:
connection_offset = -0.035
vdd_alternate = 1
connection_width = drc["minwidth_{}".format('m1')]
track_offset = 1
elif pin.name == 'gnd':
if gnd_alternate:
connection_offset = 0.035
gnd_alternate = 0
else:
connection_offset = -0.035
gnd_alternate = 1
connection_width = drc["minwidth_{}".format('m1')]
track_offset = 4
pin_width = round_to_grid(sqrt(drc["minarea_m3"]))
pin_height = round_to_grid(drc["minarea_m3"] / pin_width)
if inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_cent' or 'corner' in inst.cell_name:
if 'dummy_row' in supply_inst.name and supply_inst.mirror == 'MX':
pin_center = vector(pin.center()[0], -1 * track_offset * (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width)
elif 'dummy_row' in supply_inst.name:
pin_center = vector(pin.center()[0],inst.height + 1 * track_offset* (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, self.height), connection_width)
elif 'replica_col' in supply_inst.name and cell_inst.mirror == 'MX':
pin_center = vector(pin.center()[0], -1 * track_offset* (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width)
elif 'replica_col' in supply_inst.name:
pin_center = vector(pin.center()[0],inst.height + 1 * track_offset * (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset,self.height), connection_width)
self.add_via_stack_center(from_layer=pin.layer,
to_layer='m2',
offset=pin_center+supply_inst.ll()+cell_inst.ll() + vector(connection_offset,0))
# add well contacts to perimeter cells
for pin_name in ['vpb', 'vnb']:
for supply_inst in supply_insts:
vnb_alternate = 0
vpb_alternate = 0
for cell_inst in supply_inst.mod.insts:
inst = cell_inst.mod
for pin in inst.get_pins(pin_name):
if pin.name == 'vpb':
if vpb_alternate:
connection_offset = 0.01
vpb_alternate = 0
else:
connection_offset = 0.02
vpb_alternate = 1
connection_width = drc["minwidth_{}".format('m1')]
track_offset = 2
elif pin.name == 'vnb':
if vnb_alternate:
connection_offset = -0.01
vnb_alternate = 0
else:
connection_offset = -0.02
vnb_alternate = 1
connection_width = drc["minwidth_{}".format('m1')]
track_offset = 3
if inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_cent':
if 'dummy_row' in supply_inst.name and supply_inst.mirror == 'MX':
pin_center = vector(pin.center()[0], -1 * track_offset * (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width)
elif 'dummy_row' in supply_inst.name:
pin_center = vector(pin.center()[0],inst.height + 1 * track_offset* (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, self.height), connection_width)
elif 'replica_col' in supply_inst.name and cell_inst.mirror == 'MX':
pin_center = vector(pin.center()[0], -1 * track_offset* (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width)
elif 'replica_col' in supply_inst.name:
pin_center = vector(pin.center()[0],inst.height + 1 * track_offset * (pin_height + drc_width*2))
self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset,self.height), connection_width)
self.add_via_stack_center(from_layer=pin.layer,
to_layer='m2',
offset=pin_center+supply_inst.ll()+cell_inst.ll() + vector(connection_offset,0))
min_area = drc["minarea_{}".format('m3')]
for track,supply, offset in zip(range(1,5),['vdd','vdd','gnd','gnd'],[min_area * 6,min_area * 6, 0, 0]):
y_offset = track * (pin_height + drc_width*2)
self.add_segment_center('m2', vector(0,-y_offset), vector(self.width, -y_offset), drc["minwidth_{}".format('m2')])
self.add_segment_center('m2', vector(0,self.height + y_offset), vector(self.width, self.height + y_offset), drc["minwidth_{}".format('m2')])
self.add_power_pin(name=supply,
loc=vector(round_to_grid(sqrt(min_area))/2 + offset, -y_offset),
start_layer='m2')
self.add_power_pin(name=supply,
loc=vector(round_to_grid(sqrt(min_area))/2 + offset, self.height + y_offset),
start_layer='m2')
self.add_power_pin(name=supply,
loc=vector(self.width - round_to_grid(sqrt(min_area))/2 - offset, -y_offset),
start_layer='m2')
self.add_power_pin(name=supply,
loc=vector(self.width - round_to_grid(sqrt(min_area))/2 - offset, self.height + y_offset),
start_layer='m2')
self.offset_all_coordinates()
self.height = self.height + self.dummy_col_insts[0].lr().y * 2
for pin_name in self.all_bitline_names:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
if 'bl' in pin.name:
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=self.height)
elif 'br' in pin_name:
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(1, 0) + vector(0,pin_height + drc_width*2),
width=pin.width(),
height=self.height - 2 *(pin_height + drc_width*2))
# Replica bitlines
if len(self.rbls) > 0:
for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts):
pin_names = self.replica_columns[self.rbls[0]].all_bitline_names
mirror = self.replica_col_insts[0].mirror
for (bl_name, pin_name) in zip(names, pin_names):
pin = inst.get_pin(pin_name)
if 'rbl_bl' in bl_name:
# if mirror != "MY":
# bl_name = bl_name.replace("rbl_bl","rbl_br")
self.add_layout_pin(text=bl_name,
layer=pin.layer,
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=self.height)
elif 'rbl_br' in bl_name:
# if mirror != "MY":
# bl_name = bl_name.replace("rbl_br","rbl_bl")
self.add_layout_pin(text=bl_name,
layer=pin.layer,
offset=pin.ll().scale(1, 0) + vector(0,(pin_height + drc_width*2)),
width=pin.width(),
height=self.height - 2 *(pin_height + drc_width*2))
return
def add_wordline_pins(self):
# Wordlines to ground
self.gnd_wordline_names = []
for port in self.all_ports:
for bit in self.all_ports:
self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit))
if bit != port:
self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit))
self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl]
self.wordline_names = self.bitcell_array.wordline_names
self.all_wordline_names = self.bitcell_array.all_wordline_names
# All wordlines including dummy and RBL
self.replica_array_wordline_names = []
#self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names()))
for bit in range(self.rbl[0]):
self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]])
self.replica_array_wordline_names.extend(self.all_wordline_names)
for bit in range(self.rbl[1]):
self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]])
#self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names()))
for port in range(self.rbl[0]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
self.add_pin_list(self.all_wordline_names, "INPUT")
for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]):
self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
def create_instances(self):
""" Create the module instances used in this design """
self.supplies = ["vdd", "gnd"]
# Used for names/dimensions only
# self.cell = factory.create(module_type=OPTS.bitcell)
# Main array
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array)
self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies)
# Replica columns
self.replica_col_insts = []
for port in self.all_ports:
if port in self.rbls:
self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port),
mod=self.replica_columns[port]))
self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies + ["gnd"] + ["gnd"])
else:
self.replica_col_insts.append(None)
# Dummy rows under the bitcell array (connected with with the replica cell wl)
self.dummy_row_replica_insts = []
# Note, this is the number of left and right even if we aren't adding the columns to this bitcell array!
for port in self.all_ports:
self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port),
mod=self.dummy_row))
self.connect_inst(self.all_bitline_names + [x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies)
# Top/bottom dummy rows or col caps
self.dummy_row_insts = []
self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot",
mod=self.col_cap_bottom))
self.connect_inst(self.all_bitline_names + self.supplies + ["gnd"])
self.dummy_row_insts.append(self.add_inst(name="dummy_row_top",
mod=self.col_cap_top))
self.connect_inst(self.all_bitline_names + self.supplies + ["gnd"])
# Left/right Dummy columns
self.dummy_col_insts = []
self.dummy_col_insts.append(self.add_inst(name="dummy_col_left",
mod=self.row_cap_left))
self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + ["gnd"] + self.replica_array_wordline_names + ["gnd"] + self.supplies)
self.dummy_col_insts.append(self.add_inst(name="dummy_col_right",
mod=self.row_cap_right))
self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + ["gnd"] + self.replica_array_wordline_names + ["gnd"] + self.supplies)
+60 -235
View File
@@ -11,9 +11,9 @@ from openram.sram_factory import factory
from openram.tech import layer from openram.tech import layer
from openram import OPTS from openram import OPTS
from .sky130_bitcell_base_array import sky130_bitcell_base_array from .sky130_bitcell_base_array import sky130_bitcell_base_array
from openram.modules import pattern
from openram.modules import replica_column
class sky130_replica_column(sky130_bitcell_base_array): class sky130_replica_column(replica_column, sky130_bitcell_base_array):
""" """
Generate a replica bitline column for the replica array. Generate a replica bitline column for the replica array.
Rows is the total number of rows i the main array. Rows is the total number of rows i the main array.
@@ -23,75 +23,7 @@ class sky130_replica_column(sky130_bitcell_base_array):
""" """
def __init__(self, name, rows, rbl, replica_bit, column_offset=0): def __init__(self, name, rows, rbl, replica_bit, column_offset=0):
# Used for pin names and properties super().__init__(name=name, rows=rows, rbl=rbl, replica_bit=replica_bit, column_offset=column_offset)
self.cell = factory.create(module_type=OPTS.bitcell)
# Row size is the number of rows with word lines
self.row_size = sum(rbl) + rows
# Start of regular word line rows
self.row_start = rbl[0] + 1
# End of regular word line rows
self.row_end = self.row_start + rows
if not self.cell.end_caps:
self.row_size += 2
super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, name=name)
self.rows = rows
self.left_rbl = rbl[0]
self.right_rbl = rbl[1]
self.replica_bit = replica_bit
# left, right, regular rows plus top/bottom dummy cells
self.total_size = self.left_rbl + rows + self.right_rbl + 2
self.column_offset = column_offset
if self.rows % 2 == 0:
debug.error("Invalid number of rows {}. Number of rows must be even to connect to col ends".format(self.rows), -1)
if self.column_offset % 2 == 0:
debug.error("Invalid column_offset {}. Column offset must be odd to connect to col ends".format(self.rows), -1)
debug.check(replica_bit != 0 and replica_bit != rows,
"Replica bit cannot be the dummy row.")
debug.check(replica_bit <= self.left_rbl or replica_bit >= self.total_size - self.right_rbl - 1,
"Replica bit cannot be in the regular array.")
# if OPTS.tech_name == "sky130":
# debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0,
# "sky130 currently requires rows to be even and to start with X mirroring"
# + " (left_rbl must be even) for LVS.")
# commented out to support odd row counts while testing opc
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place_instances()
self.width = max([x.rx() for x in self.insts])
self.height = max([x.uy() for x in self.insts])
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
self.create_all_bitline_names()
#self.create_all_wordline_names(self.row_size+2)
# +2 to add fake wl pins for colends
self.create_all_wordline_names(self.row_size+1, 1)
self.add_pin_list(self.all_bitline_names, "OUTPUT")
self.add_pin_list(self.all_wordline_names, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
self.add_pin("top_gate", "INPUT")
self.add_pin("bot_gate", "INPUT")
def add_modules(self): def add_modules(self):
self.replica_cell = factory.create(module_type="replica_bitcell_1port", version="opt1") self.replica_cell = factory.create(module_type="replica_bitcell_1port", version="opt1")
@@ -99,172 +31,65 @@ class sky130_replica_column(sky130_bitcell_base_array):
self.replica_cell2 = factory.create(module_type="replica_bitcell_1port", version="opt1a") self.replica_cell2 = factory.create(module_type="replica_bitcell_1port", version="opt1a")
self.dummy_cell = factory.create(module_type="dummy_bitcell_1port", version="opt1") self.dummy_cell = factory.create(module_type="dummy_bitcell_1port", version="opt1")
self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1") self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1a")
self.strap1 = factory.create(module_type="internal", version="wlstrap")
self.strap2 = factory.create(module_type="internal", version="wlstrap_p")
self.strap3 = factory.create(module_type="internal", version="wlstrapa_p")
self.colend = factory.create(module_type="col_cap", version="colend")
self.edge_cell = self.colend
self.colenda = factory.create(module_type="col_cap", version="colenda")
self.colend_p_cent = factory.create(module_type="col_cap", version="colend_p_cent")
self.colenda_p_cent = factory.create(module_type="col_cap", version="colenda_p_cent")
self.strap = factory.create(module_type="internal", version="wlstrap")
self.strap_p = factory.create(module_type="internal", version="wlstrap_p")
self.strapa = factory.create(module_type="internal", version="wlstrapa")
self.strapa_p = factory.create(module_type="internal", version="wlstrapa_p")
def create_instances(self): def create_instances(self):
self.cell_inst = {} """ Create the module instances used in this design """
self.array_layout = [] self.all_inst={}
alternate_bitcell = (self.rows + 1) % 2 self.cell_inst={}
replica_row_opt1 = [geometry.instance("rep_00_opt1", mod=self.replica_cell, is_bitcell=True, mirror='XY')] \
+ [geometry.instance("rep_01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\
+ [geometry.instance("rep_02_opt1", mod=self.replica_cell, is_bitcell=True, mirror='MX')] \
+ [geometry.instance("rep_03_strap", mod=self.strap, is_bitcell=False, mirror='MX')]
replica_row_opt1a = [geometry.instance("rep_10_opt1a", mod=self.replica_cell2, is_bitcell=True, mirror='MY')] \
+ [geometry.instance("rep_11_strap_p", mod=self.strap_p, is_bitcell=False)] \
+ [geometry.instance("rep_12_opt1a", mod=self.replica_cell2, is_bitcell=True)] \
+ [geometry.instance("rep_13_strapaa", mod=self.strapa, is_bitcell=False)]
dummy_row_opt1 = [geometry.instance("dummy_00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='XY')] \
+ [geometry.instance("dummy_01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\
+ [geometry.instance("dummy_02_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \
+ [geometry.instance("dummy_03_strap", mod=self.strap, is_bitcell=False, mirror='MX')]
dummy_row_opt1a = [geometry.instance("dummy_10_opt1a", mod=self.dummy_cell2, is_bitcell=True, mirror='MY')] \
+ [geometry.instance("dummy_11_strap_p", mod=self.strap_p, is_bitcell=False)] \
+ [geometry.instance("dummy_12_opt1a", mod=self.dummy_cell2, is_bitcell=True)] \
+ [geometry.instance("dummy_13_strapa", mod=self.strapa, is_bitcell=False)]
bit_block = []
if self.column_offset % 2 == 0:
replica_row_opt1 = replica_row_opt1[0:2]
replica_row_opt1a = replica_row_opt1a[0:2]
dummy_row_opt1 = dummy_row_opt1[0:2]
dummy_row_opt1a = dummy_row_opt1a[0:2]
else:
replica_row_opt1 = replica_row_opt1[2:4]
replica_row_opt1a = replica_row_opt1a[2:4]
dummy_row_opt1 = dummy_row_opt1[2:4]
dummy_row_opt1a = dummy_row_opt1a[2:4]
current_row = self.row_start
for row in range(self.total_size): for row in range(self.total_size):
row_layout = [] # Regular array cells are replica cells
name="rbc_{0}".format(row) # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell.
# Top/bottom cell are always dummy cells. # All other cells are dummies
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl) if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end):
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell. if current_row % 2 == 1:
if (row > self.left_rbl and row < self.total_size - 1 or row == self.replica_bit): pattern.append_row_to_block(bit_block, replica_row_opt1)
if alternate_bitcell == 0:
row_layout.append(self.replica_cell)
self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell)
self.connect_inst(self.get_bitcell_pins(row, 0))
row_layout.append(self.strap2)
self.add_inst(name=name + "_strap_p", mod=self.strap2)
self.connect_inst(self.get_strap_pins(row, 0, name + "_strap_p"))
alternate_bitcell = 1
else: else:
row_layout.append(self.replica_cell2) pattern.append_row_to_block(bit_block, replica_row_opt1a)
self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell2)
self.connect_inst(self.get_bitcell_pins(row, 0))
row_layout.append(self.strap3)
self.add_inst(name=name + "_strap", mod=self.strap3)
self.connect_inst(self.get_strap_pins(row, 0))
alternate_bitcell = 0
elif (row == 0):
row_layout.append(self.colend)
self.cell_inst[row]=self.add_inst(name=name, mod=self.colend)
self.connect_inst(self.get_col_cap_pins(row, 0))
row_layout.append(self.colend_p_cent)
self.add_inst(name=name + "_cap", mod=self.colend_p_cent)
self.connect_inst(self.get_col_cap_p_pins(row, 0))
elif (row == self.total_size - 1):
row_layout.append(self.colenda)
self.cell_inst[row]=self.add_inst(name=name, mod=self.colenda)
self.connect_inst(self.get_col_cap_pins(row, 0))
row_layout.append(self.colenda_p_cent)
self.add_inst(name=name + "_cap", mod=self.colenda_p_cent)
self.connect_inst(self.get_col_cap_p_pins(row, 0))
self.array_layout.append(row_layout)
def place_instances(self, name_template="", row_offset=0):
col_offset = self.column_offset
yoffset = 0.0
for row in range(row_offset, len(self.array_layout) + row_offset):
xoffset = 0.0
for col in range(col_offset, len(self.array_layout[row]) + col_offset):
self.place_inst = self.insts[(col - col_offset) + (row - row_offset) * len(self.array_layout[row - row_offset])]
if row == row_offset or row == (len(self.array_layout) + row_offset -1):
if row == row_offset:
self.place_inst.place(offset=[xoffset, yoffset + self.colend.height], mirror="MX")
else:
self.place_inst.place(offset=[xoffset, yoffset])
elif col % 2 == 0:
if row % 2 == 0:
self.place_inst.place(offset=[xoffset, yoffset + self.place_inst.height], mirror="MX")
else:
self.place_inst.place(offset=[xoffset, yoffset])
else:
if row % 2 == 0:
self.place_inst.place(offset=[xoffset + self.place_inst.width, yoffset + self.place_inst.height], mirror="XY")
else:
self.place_inst.place(offset=[xoffset + self.place_inst.width, yoffset], mirror="MY")
xoffset += self.place_inst.width
if row == row_offset:
yoffset += self.colend.height
else: else:
yoffset += self.place_inst.height if current_row % 2 == 1:
pattern.append_row_to_block(bit_block, dummy_row_opt1)
else:
pattern.append_row_to_block(bit_block, dummy_row_opt1a)
current_row += 1
self.pattern = pattern(self, "replica_column", bit_block, num_rows=self.total_size, num_cols=len(replica_row_opt1), name_template="rbc_r{0}_c{1}")
self.pattern.connect_array_raw()
self.width = max([x.rx() for x in self.insts])
self.height = max([x.uy() for x in self.insts])
def add_layout_pins(self):
""" Add the layout pins """
for port in self.all_ports:
bl_pin = self.cell_inst[2].get_pin(self.cell.get_bl_name(port))
self.add_layout_pin(text="bl_{0}_{1}".format(port, 0),
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
bl_pin = self.cell_inst[2].get_pin(self.cell.get_br_name(port))
self.add_layout_pin(text="br_{0}_{1}".format(port, 0),
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
row_range_max = self.total_size - 1
row_range_min = 1
for port in self.all_ports:
for row in range(row_range_min, row_range_max):
wl_pin = self.cell_inst[row].get_pin(self.cell.get_wl_name(port))
self.add_layout_pin(text="wl_{0}_{1}".format(port, row_range_max-row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
# for colend in [self.cell_inst[0], self.cell_inst[self.row_size]]:
# inst = self.cell_inst[row]
# for pin_name in ["top_gate", "bot_gate"]:
# pin = inst.get_pin("gate")
# self.add_layout_pin(text=pin_name,
# layer=pin.layer,
# offset=pin.ll(),
# width=pin.width(),
# height=pin.height())
for row in range(self.row_size + 2):
inst = self.cell_inst[row]
# add only 1 label per col
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
#if row == 2:
if 'VPB' or 'vpb' in self.cell_inst[row].mod.pins:
pin = inst.get_pin("vpb")
self.objs.append(geometry.rectangle(layer["nwell"],
pin.ll(),
pin.width(),
pin.height()))
self.objs.append(geometry.label("vdd", layer["nwell"], pin.center()))
if 'VNB' or 'vnb' in self.cell_inst[row].mod.pins:
try:
from openram.tech import layer_override
if layer_override['VNB']:
pin = inst.get_pin("vnb")
self.add_label("gnd", pin.layer, pin.center())
self.objs.append(geometry.rectangle(layer["pwellp"],
pin.ll(),
pin.width(),
pin.height()))
self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center()))
except:
pin = inst.get_pin("vnb")
self.add_label("gnd", pin.layer, pin.center())
def exclude_all_but_replica(self):
"""
Excludes all bits except the replica cell (self.replica_bit).
"""
for row, cell in self.cell_inst.items():
if row != self.replica_bit:
self.graph_inst_exclude.add(cell)
+78 -111
View File
@@ -5,140 +5,107 @@
# All rights reserved. # All rights reserved.
# #
from openram.base import geometry
from openram.sram_factory import factory from openram.sram_factory import factory
from openram import OPTS from openram import OPTS
from .sky130_bitcell_base_array import sky130_bitcell_base_array from .sky130_bitcell_base_array import sky130_bitcell_base_array
from openram.modules.row_cap_array import row_cap_array
from openram.modules.pattern import pattern
from math import ceil
class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array):
class sky130_row_cap_array(sky130_bitcell_base_array):
""" """
Generate a dummy row/column for the replica array. Generate a dummy row/column for the replica array.
""" """
def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""):
# Don't call the regular col-cap_array constructor since we don't want its constructor, just super().__init__(rows, cols, column_offset=column_offset, location=location, name=name)
# some of it's useful member functions
sky130_bitcell_base_array.__init__(self, rows=rows, cols=cols, column_offset=column_offset, name=name)
self.rows = rows
self.cols = cols
self.column_offset = column_offset
self.mirror = mirror self.mirror = mirror
self.create_netlist() self.location = location
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
self.create_all_wordline_names()
# This module has no bitlines
# self.create_all_bitline_names()
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place_array("dummy_r{0}_c{1}", self.mirror)
self.add_layout_pins()
self.width = max([x.rx() for x in self.insts])
self.height = max([x.uy() for x in self.insts])
self.add_boundary()
self.DRC_LVS()
def add_modules(self): def add_modules(self):
""" Add the modules used in this design """ """ Add the modules used in this design """
if self.column_offset == 0: if self.location == "left":
self.top_corner = factory.create(module_type="corner", location="ul") self.top_corner = factory.create(module_type="corner", location="ul")
self.bottom_corner =factory.create(module_type="corner", location="ll") self.bottom_corner =factory.create(module_type="corner", location="ll")
self.rowend1 = factory.create(module_type="row_cap", version="rowend_replica") #self.rowend1 = factory.create(module_type="row_cap", version="rowend_replica")
self.rowend2 = factory.create(module_type="row_cap", version="rowenda_replica") #self.rowend2 = factory.create(module_type="row_cap", version="rowenda_replica")
else: else:
self.top_corner = factory.create(module_type="corner", location="ur") self.top_corner = factory.create(module_type="corner", location="ur")
self.bottom_corner = factory.create(module_type="corner", location="lr") self.bottom_corner = factory.create(module_type="corner", location="lr")
self.rowend1 = factory.create(module_type="row_cap", version="rowend") #self.rowend1 = factory.create(module_type="row_cap", version="rowend")
self.rowend2 = factory.create(module_type="row_cap", version="rowenda") #self.rowend2 = factory.create(module_type="row_cap", version="rowenda")
self.rowend = factory.create(module_type="row_cap", version="rowend")
self.rowenda = factory.create(module_type="row_cap", version="rowenda")
self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1")
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ self.all_inst={}
self.cell_inst = {} self.cell_inst={}
self.array_layout = []
alternate_bitcell = (self.rows + 1) % 2 bit_block = []
for row in range(self.rows + 2):
row_layout = [] if self.location == "left":
name="rca_{0}".format(row) top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False, mirror="MY")
# Top/bottom cell are always dummy cells. bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="XY")
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl) rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True, mirror="XY")
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell. rowenda = geometry.instance("row_cap_rowenda", mod=self.rowenda, is_bitcell=True, mirror="MY")
elif self.location == "right":
top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False)
bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="MX")
rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True, mirror="MX")
rowenda = geometry.instance("row_cap_rowenda", mod=self.rowenda, is_bitcell=True)
if (row < self.rows + 1 and row > 0): pattern.append_row_to_block(bit_block, [top_corner])
for row in range(1, self.row_size-1):
if alternate_bitcell == 0: if row % 2 == 1:
row_layout.append(self.rowend1) pattern.append_row_to_block(bit_block, [rowend])
self.cell_inst[row]=self.add_inst(name=name, mod=self.rowend1)
self.connect_inst(["wl_0_{}".format(row - 1), "vdd"])
alternate_bitcell = 1
else:
row_layout.append(self.rowend2)
self.cell_inst[row] = self.add_inst(name=name, mod=self.rowend2)
self.connect_inst(["wl_0_{}".format(row - 1), "vdd"])
alternate_bitcell = 0
elif (row == 0):
row_layout.append(self.bottom_corner)
self.cell_inst[row]=self.add_inst(name=name, mod=self.bottom_corner)
self.connect_inst(self.get_corner_pins())
elif (row == self.rows + 1):
row_layout.append(self.top_corner)
self.cell_inst[row]=self.add_inst(name=name, mod=self.top_corner)
self.connect_inst(self.get_corner_pins())
self.array_layout.append(row_layout)
def place_array(self, name_template, row_offset=0):
xoffset = 0.0
yoffset = 0.0
for row in range(len(self.insts)):
inst = self.insts[row]
if row == 0:
inst.place(offset=[xoffset, yoffset + inst.height], mirror="MX")
elif row == len(self.insts)-1:
inst.place(offset=[xoffset, yoffset])
else: else:
if row % 2 ==0: pattern.append_row_to_block(bit_block, [rowenda])
inst.place(offset=[xoffset, yoffset + inst.height], mirror="MX") pattern.append_row_to_block(bit_block, [bottom_corner])
else: self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}")
inst.place(offset=[xoffset, yoffset]) self.pattern.connect_array_raw()
yoffset += inst.height
def get_bitcell_pins(self, row, col):
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
def add_pins(self): bitcell_pins = []
for row in range(self.rows + 2): bitcell_pins.append("vdd") # vdd
bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))])
#bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))])
return bitcell_pins
def get_strap_pins(self, row, col):
strap_pins = []
strap_pins.append("vdd") # vdd
strap_pins.append("vdd") # vpb
strap_pins.append("gnd") # vnb
return strap_pins
def create_all_wordline_names(self, row_size=None, start_row=0):
if row_size == None:
row_size = self.row_size
row_size = row_size - 2
for row in range(start_row, row_size):
for port in self.all_ports: for port in self.all_ports:
self.add_pin("wl_{}_{}".format(port, row), "OUTPUT") self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_layout_pins(self): self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
""" Add the layout pins """
for row in range(0, self.rows + 1): def create_layout(self):
if row > 0 and row < self.rows + 1:
wl_pin = self.cell_inst[row].get_pin("wl")
self.add_layout_pin(text="wl_0_{0}".format(row -1),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
# Add vdd/gnd via stacks self.place_array()
for row in range(1, self.rows): self.add_layout_pins()
inst = self.cell_inst[row]
for pin_name in ["vdd", "gnd"]: self.add_boundary()
for pin in inst.get_pins(pin_name): self.DRC_LVS()
self.copy_layout_pin(inst, pin_name)
+1
View File
@@ -817,6 +817,7 @@ blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell",
"sky130_fd_bd_sram__sram_sp_cell_opt1a", "sky130_fd_bd_sram__sram_sp_cell_opt1a",
"sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy", "sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy",
"sky130_fd_bd_sram__openram_sp_cell_opt1_dummy",
"sky130_fd_bd_sram__sram_sp_cell_opt1_ce", "sky130_fd_bd_sram__sram_sp_cell_opt1_ce",
"sky130_fd_bd_sram__sram_sp_cell_opt1", "sky130_fd_bd_sram__sram_sp_cell_opt1",
"sky130_fd_bd_sram__openram_sp_cell_opt1_replica", "sky130_fd_bd_sram__openram_sp_cell_opt1_replica",