Merge branch 'singleport_refactor' into array_gen

This commit is contained in:
Jesse Cirimelli-Low 2025-02-24 23:26:28 -08:00
commit f3c1c5fbb2
35 changed files with 1133 additions and 2828 deletions

View File

@ -13,7 +13,7 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git
# Use this for development
#SRAM_LIB_GIT_REPO ?= git@github.com: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_COMMIT ?= dd64256961317205343a3fd446908b42bafba388
SRAM_LIB_GIT_COMMIT ?= 8dccd8d8ddb0a9de1b02207b2cd0c0d697807aa9
SKY130_PDK ?= $(PDK_ROOT)/sky130A
GF180_PDK ?= $(PDK_ROOT)/gf180mcuD

View File

@ -66,6 +66,8 @@ class contact(hierarchy_design):
self.offset = vector(0, 0)
self.implant_type = implant_type
self.well_type = well_type
# Module does not have pins, but has empty pin list.
self.pins = []
self.create_layout()
def create_layout(self):

View File

@ -164,7 +164,7 @@ class instance(geometry):
An instance of a module with a specified location, rotation,
spice pins, and spice nets
"""
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False):
"""Initializes an instance to represent a module"""
super().__init__()
debug.check(mirror not in ["R90", "R180", "R270"],
@ -176,6 +176,8 @@ class instance(geometry):
self.rotate = rotate
self.offset = vector(offset).snap_to_grid()
self.mirror = mirror
self.is_bitcell = is_bitcell
# track if the instance's spice pin connections have been made
self.connected = False
@ -183,10 +185,11 @@ class instance(geometry):
# change attributes in these spice objects
self.spice_pins = copy.deepcopy(self.mod.pins)
self.spice_nets = copy.deepcopy(self.mod.nets)
for pin in self.spice_pins.values():
pin.set_inst(self)
for net in self.spice_nets.values():
net.set_inst(self)
if "contact" not in mod.name:
for pin in self.spice_pins.values():
pin.set_inst(self)
for net in self.spice_nets.values():
net.set_inst(self)
if OPTS.netlist_only:
self.width = 0

View File

@ -9,6 +9,7 @@ import sys
import os
import re
from math import sqrt
from copy import deepcopy
from openram import debug
from openram.gdsMill import gdsMill
from openram import tech
@ -460,21 +461,32 @@ class layout():
for pin in pin_list:
pin.rect = [pin.ll() - offset, pin.ur() - offset]
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False):
""" Adds an instance of a mod to this module """
# Contacts are not really instances, so skip them
if "contact" not in mod.name:
# Check that the instance name is unique
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name))
self.mods.add(mod)
self.inst_names.add(name)
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate, is_bitcell))
debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons
# debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
return self.insts[-1]
def add_existing_inst(self, inst, name):
#new_inst = deepcopy(inst)
new_inst = self.add_inst(name, inst.mod, offset=inst.offset, mirror=inst.mirror, rotate=inst.rotate, is_bitcell=inst.is_bitcell)
#new_inst.mod = inst.mod
self.mods.add(new_inst.mod)
#if name:
# new_inst.name = name
self.inst_names.add(new_inst.name)
#self.insts.append(new_inst)
debug.info(3, "adding existing instance{}".format(self.insts[-1]))
return self.insts[-1]
def get_inst(self, name):
""" Retrieve an instance by name """
for inst in self.insts:
@ -2149,6 +2161,13 @@ class layout():
# Add the gnd ring
self.add_ring([ll, ur])
def reset_coordinates(self):
ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts]))
self.translate_all(ll)
self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts])
self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts])
def add_ring(self, bbox=None, width_mult=8, offset=0):
"""
Add a ring around the bbox
@ -2201,7 +2220,7 @@ class layout():
size=(supply_vias,
supply_vias))
def add_power_ring(self):
def add_power_ring(self, h_layer="m2", v_layer="m1", top=True, bottom=True, left=True, right=True):
"""
Create vdd and gnd power rings around an area of the bounding box
argument. Must have a supply_rail_width and supply_rail_pitch
@ -2209,118 +2228,120 @@ class layout():
left/right/top/bottom vdd/gnd center offsets for use in other
modules..
"""
[ll, ur] = self.bbox
supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width
supply_rail_spacing = self.supply_rail_pitch
height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing
width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
# LEFT vertical rails
offset = ll + vector(-2 * self.supply_rail_pitch,
-2 * self.supply_rail_pitch)
left_gnd_pin = self.add_layout_pin(text="gnd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
if left:
offset = ll + vector(-2*self.supply_rail_pitch,
-2*self.supply_rail_pitch)
self.left_gnd_pin = self.add_layout_pin(text="gnd",
layer=v_layer,
offset=offset,
width=self.supply_rail_width,
height=height + 2 * supply_rail_spacing)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
left_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
self.left_vdd_pin = self.add_layout_pin(text="vdd",
layer=v_layer,
offset=offset,
width=self.supply_rail_width,
height=height)
# RIGHT vertical rails
offset = vector(ur.x, ll.y) + vector(0, -2 * self.supply_rail_pitch)
right_gnd_pin = self.add_layout_pin(text="gnd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
if right:
# RIGHT vertical railsteac a 460
offset = vector(ur.x, ll.y) + vector(2 * self.supply_rail_pitch - self.supply_rail_width,
-2 * self.supply_rail_pitch)
self.right_gnd_pin = self.add_layout_pin(text="gnd",
layer=v_layer,
offset=offset,
width=self.supply_rail_width,
height=height + 2* supply_rail_spacing)
offset = vector(ur.x, ll.y) + vector(self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
right_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
offset = vector(ur.x, ll.y) + vector(1 * self.supply_rail_pitch - self.supply_rail_width,
-1 * self.supply_rail_pitch)
self.right_vdd_pin = self.add_layout_pin(text="vdd",
layer=v_layer,
offset=offset,
width=self.supply_rail_width,
height=height)
# BOTTOM horizontal rails
offset = ll + vector(-2 * self.supply_rail_pitch,
-2 * self.supply_rail_pitch)
bottom_gnd_pin = self.add_layout_pin(text="gnd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
if bottom:
# BOTTOM horizontal rails
offset = ll + vector(-2 * self.supply_rail_pitch,
-2 * self.supply_rail_pitch)
self.bottom_gnd_pin = self.add_layout_pin(text="gnd",
layer=h_layer,
offset=offset,
width=width + 2 * supply_rail_spacing,
height=self.supply_rail_width)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
bottom_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
self.bottom_vdd_pin = self.add_layout_pin(text="vdd",
layer=h_layer,
offset=offset,
width=width,
height=self.supply_rail_width)
if top:
# TOP horizontal rails
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
2 * self.supply_rail_pitch - self.supply_rail_width)
self.top_gnd_pin = self.add_layout_pin(text="gnd",
layer=h_layer,
offset=offset,
width=width + 2 * supply_rail_spacing,
height=self.supply_rail_width)
# TOP horizontal rails
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
0)
top_gnd_pin = self.add_layout_pin(text="gnd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch,
self.supply_rail_pitch)
top_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch,
1 * self.supply_rail_pitch - self.supply_rail_width)
self.top_vdd_pin = self.add_layout_pin(text="vdd",
layer=h_layer,
offset=offset,
width=width,
height=self.supply_rail_width)
# Remember these for connecting things in the design
self.left_gnd_x_center = left_gnd_pin.cx()
self.left_vdd_x_center = left_vdd_pin.cx()
self.right_gnd_x_center = right_gnd_pin.cx()
self.right_vdd_x_center = right_vdd_pin.cx()
self.bottom_gnd_y_center = bottom_gnd_pin.cy()
self.bottom_vdd_y_center = bottom_vdd_pin.cy()
self.top_gnd_y_center = top_gnd_pin.cy()
self.top_vdd_y_center = top_vdd_pin.cy()
# Find the number of vias for this pitch
self.supply_vias = 1
while True:
c = factory.create(module_type="contact",
layer_stack=self.m1_stack,
dimensions=(self.supply_vias, self.supply_vias))
if c.second_layer_width < self.supply_rail_width and c.second_layer_height < self.supply_rail_width:
self.supply_vias += 1
else:
self.supply_vias -= 1
break
via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center),
vector(self.left_gnd_x_center, self.top_gnd_y_center),
vector(self.right_gnd_x_center, self.bottom_gnd_y_center),
vector(self.right_gnd_x_center, self.top_gnd_y_center),
vector(self.left_vdd_x_center, self.bottom_vdd_y_center),
vector(self.left_vdd_x_center, self.top_vdd_y_center),
vector(self.right_vdd_x_center, self.bottom_vdd_y_center),
vector(self.right_vdd_x_center, self.top_vdd_y_center)]
if left:
self.left_gnd_x_center = self.left_gnd_pin.cx()
self.left_vdd_x_center = self.left_vdd_pin.cx()
if right:
self.right_gnd_x_center = self.right_gnd_pin.cx()
self.right_vdd_x_center = self.right_vdd_pin.cx()
if bottom:
self.bottom_gnd_y_center = self.bottom_gnd_pin.cy()
self.bottom_vdd_y_center = self.bottom_vdd_pin.cy()
if top:
self.top_gnd_y_center = self.top_gnd_pin.cy()
self.top_vdd_y_center = self.top_vdd_pin.cy()
via_points = []
if left and bottom:
via_points.append((self.left_gnd_x_center, self.bottom_gnd_y_center))
if left and top:
via_points.append(vector(self.left_gnd_x_center, self.top_gnd_y_center))
if right and bottom:
via_points.append(vector(self.right_gnd_x_center, self.bottom_gnd_y_center))
if right and top:
via_points.append(vector(self.right_gnd_x_center, self.top_gnd_y_center))
if left and bottom:
via_points.append(vector(self.left_vdd_x_center, self.bottom_vdd_y_center))
if left and top:
via_points.append(vector(self.left_vdd_x_center, self.top_vdd_y_center))
if right and bottom:
via_points.append(vector(self.right_vdd_x_center, self.bottom_vdd_y_center))
if right and top:
via_points.append((self.right_vdd_x_center, self.top_vdd_y_center))
for pt in via_points:
self.add_via_center(layers=self.m1_stack,
offset=pt,
size=(self.supply_vias,
self.supply_vias))
self.add_via_stack_center(offset=pt,
from_layer=h_layer,
to_layer=v_layer,
min_area=True)
def pdf_write(self, pdf_name):
"""
Display the layout to a PDF file.

View File

@ -50,6 +50,7 @@ class spice():
if not os.path.exists(self.lvs_file):
self.lvs_file = self.sp_file
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
# Holds subckts/mods for this module
self.mods = set()
# Holds the pins for this module (in order)
@ -699,7 +700,8 @@ class spice():
def get_instance_connections(self):
conns = []
for inst in self.insts:
conns.append(inst.get_connections())
if "contact" not in inst.name:
conns.append(inst.get_connections())
return conns
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):

View File

@ -41,7 +41,7 @@ from .local_bitcell_array import *
from .nand2_dec import *
from .nand3_dec import *
from .nand4_dec import *
from .orig_bitcell_array import *
#from .orig_bitcell_array import *
from .pand2 import *
from .pand3 import *
from .pand4 import *

View File

@ -10,7 +10,9 @@ from openram.tech import drc, spice
from openram.sram_factory import factory
from openram import OPTS
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):
"""
@ -42,7 +44,7 @@ class bitcell_array(bitcell_base_array):
def create_layout(self):
self.place_array("bit_r{0}_c{1}")
self.place_array()
self.add_layout_pins()
@ -57,18 +59,26 @@ class bitcell_array(bitcell_base_array):
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(row, col))
self.cell_inst={}
if self.cell.mirror.y:
core_block = [[0 for x in range(2)] 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')
core_block[0][1] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY')
core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY')
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.trim_insts.add(name)
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.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):
"""Power of Bitcell array and bitline in nW."""

View File

@ -6,10 +6,11 @@
# All rights reserved.
#
from openram import debug
from openram.base.geometry import instance
from openram.base import design
from openram.sram_factory import factory
from openram import OPTS
from openram.modules import pattern
class bitcell_base_array(design):
"""
@ -149,7 +150,7 @@ class bitcell_base_array(design):
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])
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),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
@ -157,14 +158,17 @@ class bitcell_base_array(design):
height=wl_pin.height())
def route_supplies(self):
for inst in self.cell_inst.values():
for inst in self.insts:
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):
""" Add the layout pins """
self.add_bitline_pins()
self.add_wl_pins()
if self.get_bitline_names():
self.add_bitline_pins()
if self.get_wordline_names():
self.add_wl_pins()
def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset
@ -184,32 +188,8 @@ class bitcell_base_array(design):
dir_x = True
return (tempy, dir_x)
def place_array(self, name_template, row_offset=0):
# We increase it by a well enclosure so the precharges don't overlap our wells
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 place_array(self):
self.pattern.place_array()
def get_column_offsets(self):
"""

View File

@ -51,10 +51,10 @@ class capped_replica_bitcell_array(bitcell_base_array):
self.rbls = self.left_rbl + self.right_rbl
# 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 not self.cell.end_caps:
self.extra_rows += 2
#if not self.cell.end_caps:
# self.extra_rows += 2
self.create_netlist()
if not OPTS.netlist_only:
@ -85,7 +85,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=self.column_size + len(self.rbls),
rows=1,
# dummy column + left replica column(s)
column_offset=1,
column_offset=0,
mirror=0,
location="top")
@ -93,7 +93,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=self.column_size + len(self.rbls),
rows=1,
# dummy column + left replica column(s)
column_offset=1,
column_offset=0,
mirror=0,
location="bottom")
@ -104,7 +104,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=1,
column_offset=0,
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,
cols=1,
@ -112,9 +112,9 @@ class capped_replica_bitcell_array(bitcell_base_array):
# + left replica column(s)
# + bitcell columns
# + 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,
mirror=(self.rbl[0] + 1) % 2)
location="right")
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.
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]))
# 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
self.replica_bitcell_array_inst.place(offset=self.unused_offset)
self.replica_bitcell_array_inst.place(offset=0)
self.add_end_caps()
# shift everything up and right to account for cap cells
self.translate_all(self.bitcell_offset.scale(-1, -1))
self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x
self.height = self.dummy_row_insts[1].uy()
self.add_layout_pins()
ll = vector(-1 * self.dummy_col_insts[0].width, -1 * self.dummy_row_insts[0].height)
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.capped_rba_height = self.dummy_col_insts[0].height
#self.route_power_ring(self.supply_stack[2], self.supply_stack[0])
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.reset_coordinates()
self.add_layout_pins()
self.add_boundary()
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):
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):
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):
return self.dummy_row_insts[1].by()
@ -261,7 +255,6 @@ class capped_replica_bitcell_array(bitcell_base_array):
def get_replica_right(self):
return self.dummy_col_insts[1].rx()
def get_column_offsets(self):
"""
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):
""" 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, flip_dummy) + self.replica_bitcell_array_inst.ul()
self.dummy_row_insts[1].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# Far top dummy row
offset = self.replica_bitcell_array_inst.ul()
self.dummy_row_insts[1].place(offset=offset)
# 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, flip_dummy - 1) + self.unused_offset
self.dummy_row_insts[0].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# Far bottom dummy row
dummy_row_height = vector(0, self.dummy_row_insts[0].height)
offset = self.replica_bitcell_array_inst.ll() - dummy_row_height
self.dummy_row_insts[0].place(offset=offset)
# 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(-1, -1) + self.unused_offset
self.dummy_col_insts[0].place(offset=dummy_col_offset)
dummy_col_width = vector(self.dummy_col_insts[0].width, 0)
offset = self.dummy_row_insts[0].ll() - dummy_col_width
self.dummy_col_insts[0].place(offset=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(0, -1) + self.replica_bitcell_array_inst.lr()
self.dummy_col_insts[1].place(offset=dummy_col_offset)
offset = self.dummy_row_insts[0].lr()
self.dummy_col_insts[1].place(offset=offset)
def add_layout_pins(self):
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:
# wordlines
pin_offset = pin.ll().scale(0, 1)
pin_width = self.width
pin_width = self.capped_rba_width
pin_height = pin.height()
else:
# bitlines
pin_offset = pin.ll().scale(1, 0)
pin_width = pin.width()
pin_height = self.height
pin_height = self.capped_rba_height
self.add_layout_pin(text=pin_name,
layer=pin.layer,
@ -321,56 +311,83 @@ class capped_replica_bitcell_array(bitcell_base_array):
bitcell = factory.create(module_type="pbitcell")
else:
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
gnd_dir = bitcell.gnd_dir
array_pins = inst.get_pins("gnd")
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
supply_insts = self.dummy_col_insts + self.dummy_row_insts
array_pins = inst.get_pins("gnd")
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
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)
array_pins = inst.get_pins("gnd")
for array_pin in array_pins:
supply_pin = self.left_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]))
if right:
inst = self.dummy_col_insts[1]
if "vdd" in inst.mod.pins:
array_pins = inst.get_pins("vdd")
for array_pin in array_pins:
supply_pin = self.right_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]))
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):
"""
Connect the unused RBL and dummy wordlines to gnd

View File

@ -79,24 +79,24 @@ class col_cap_array(bitcell_base_array):
return bitcell_pins
def add_layout_pins(self):
""" Add the layout pins """
# def add_layout_pins(self):
# """ 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 cell_column in column_list:
bl_pin = self.cell_inst[0, col].get_pin(cell_column)
self.add_layout_pin(text=cell_column + "_{0}".format(col),
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(),
height=self.height)
# for col in range(self.column_size):
# for cell_column in column_list:
# bl_pin = self.cell_inst[0, col].get_pin(cell_column)
# self.add_layout_pin(text=cell_column + "_{0}".format(col),
# layer=bl_pin.layer,
# offset=bl_pin.ll().scale(1, 0),
# width=bl_pin.width(),
# height=self.height)
# Add vdd/gnd via stacks
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"]:
for pin in inst.get_pins(pin_name):
self.copy_layout_pin(inst, pin_name)
# # Add vdd/gnd via stacks
# 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"]:
# for pin in inst.get_pins(pin_name):
# self.copy_layout_pin(inst, pin_name)

View File

@ -6,16 +6,20 @@
from openram.sram_factory import factory
from openram import OPTS
from .bitcell_base_array import bitcell_base_array
from openram.base import geometry
from .pattern import pattern
class dummy_array(bitcell_base_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()
if not OPTS.netlist_only:
self.create_layout()
@ -32,7 +36,7 @@ class dummy_array(bitcell_base_array):
def create_layout(self):
self.place_array("dummy_r{0}_c{1}", self.mirror)
self.place_array()
self.add_layout_pins()
@ -50,13 +54,21 @@ class dummy_array(bitcell_base_array):
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.dummy_cell)
self.connect_inst(self.get_bitcell_pins(row, col))
self.cell_inst={}
if self.cell.mirror.y:
core_block = [[0 for x in range(2)] 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')
core_block[(0+self.mirror) %2][1] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY')
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):
# 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("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):
# FIXME: This appears to be old code from previous characterization. Needs to be updated.
wl_wire = self.gen_wl_wire()

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
compiler/modules/pattern.py Normal file
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

View File

@ -156,13 +156,15 @@ class pgate(design):
return via
def extend_wells(self):
def extend_wells(self, width=None):
""" Extend the n/p wells to cover whole cell """
# This should match the cells in the cell library
self.nwell_yoffset = 0.48 * self.height
full_height = self.height + 0.5 * self.m1_width
if width == None:
width = self.width + 2 * self.well_extend_active
# FIXME: float rounding problem
if "nwell" in layer:
@ -173,12 +175,12 @@ class pgate(design):
nwell_height = nwell_max_offset - self.nwell_yoffset
self.add_rect(layer="nwell",
offset=nwell_position,
width=self.width + 2 * self.well_extend_active,
width=width,
height=nwell_height)
if "vtg" in layer:
self.add_rect(layer="vtg",
offset=nwell_position,
width=self.width + 2 * self.well_extend_active,
width=width,
height=nwell_height)
# Start this half a rail width below the cell
@ -189,12 +191,12 @@ class pgate(design):
pwell_height = self.nwell_yoffset - pwell_position.y
self.add_rect(layer="pwell",
offset=pwell_position,
width=self.width + 2 * self.well_extend_active,
width=width,
height=pwell_height)
if "vtg" in layer:
self.add_rect(layer="vtg",
offset=pwell_position,
width=self.width + 2 * self.well_extend_active,
width=width,
height=pwell_height)
if cell_props.pgate.add_implants:

View File

@ -4,6 +4,8 @@
# All rights reserved.
#
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 contact
from openram.sram_factory import factory
@ -74,7 +76,7 @@ class replica_bitcell_array(bitcell_base_array):
""" Array and dummy/replica columns """
# 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,
rows=self.row_size)
@ -87,11 +89,11 @@ class replica_bitcell_array(bitcell_base_array):
if port in self.left_rbl:
# These go top down starting from the bottom of the bitcell array.
replica_bit = self.rbl[0] - port - 1
column_offset = len(self.left_rbl)
column_offset = 0
elif port in self.right_rbl:
# These go bottom up starting from the top of the bitcell array.
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:
continue
@ -102,14 +104,24 @@ class replica_bitcell_array(bitcell_base_array):
replica_bit=replica_bit)
# 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,
rows=1,
# cap column + left replica column
# FIXME: these col offsets should really start at 0 because
# this is the left edge of the array... but changing them all is work
column_offset=1 + len(self.left_rbl),
mirror=0)
row_offset=row_offset,
column_offset=len(self.left_rbl),
mirror=mirror)
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
if self.rbl[port] != 0:
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)
else:
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.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
self.bitcell_array_inst.place(offset=0)
self.bitcell_array_inst.place(offset=(0,0))
self.add_replica_columns()
# 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(-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.route_supplies()
self.height = (sum(self.rbl) + self.row_size) * self.cell.height
self.width = (len(self.rbls) + self.column_size) * self.cell.width
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])
self.add_boundary()
@ -280,25 +292,24 @@ class replica_bitcell_array(bitcell_base_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])
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)
# 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])
offset = self.bitcell_array_inst.lr() + rbc_dimension.scale(bit, -self.rbl[0])
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.dummy_row_replica_insts[bit].place(offset=dummy_offset,
mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0")
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)
# 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")
dummy_offset = self.bitcell_array_inst.ul()
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset)
def add_layout_pins(self):
""" 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
# 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()):
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())
if self.rbl != [0,0]:
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
for (wl_name, pin_name) in zip(names, self.dummy_rows[0].get_wordline_names()):
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())
# Main array bl/br
for pin_name in self.all_bitline_names:
@ -347,12 +359,6 @@ class replica_bitcell_array(bitcell_base_array):
width=pin.width(),
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):
"""Power of Bitcell array and bitline in nW."""
# Dynamic Power from Bitline

View File

@ -9,7 +9,8 @@ from openram.sram_factory import factory
from openram.tech import layer_properties as layer_props
from openram import OPTS
from .bitcell_base_array import bitcell_base_array
from openram.base import geometry
from openram.modules import pattern
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,
"Replica bit cannot be in the regular array.")
if layer_props.replica_column.even_rows:
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 odd) for LVS.")
#if layer_props.replica_column.even_rows:
# 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 odd) for LVS.")
self.create_netlist()
if not OPTS.netlist_only:
@ -59,10 +60,7 @@ class replica_column(bitcell_base_array):
self.create_instances()
def create_layout(self):
self.place_instances()
self.height = self.cell_inst[-1].uy()
self.width = self.cell_inst[0].rx()
self.place_array()
self.add_layout_pins()
@ -88,103 +86,35 @@ class replica_column(bitcell_base_array):
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
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):
name = "rbc_{0}".format(row)
# Regular array cells are replica cells
# Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell.
# All other cells are dummies
if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end):
self.cell_inst.append(self.add_inst(name=name,
mod=self.replica_cell))
self.connect_inst(self.get_bitcell_pins(row, 0))
if current_row % 2 == 0:
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True)
else:
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX')
else:
self.cell_inst.append(self.add_inst(name=name,
mod=self.dummy_cell))
self.connect_inst(self.get_bitcell_pins(row, 0))
if current_row % 2 == 0:
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True)
else:
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX')
def place_instances(self):
# Flip the mirrors if we have an odd number of replica+dummy rows at the bottom
# 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:
current_row += 1
if self.cell.mirror.y:
for row in range(self.total_size):
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),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1),
width=self.width,
height=wl_pin.height())
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
if self.column_offset % 2 == 0:
if core_block[row][0].mirror=='MX':
core_block[row][0].mirror='XY'
else:
core_block[row][0].mirror='MY'
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 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):
if row != self.replica_bit:
self.graph_inst_exclude.add(cell)
self.graph_inst_exclude.add(self.cell_inst[cell])

View File

@ -12,10 +12,11 @@ class row_cap_array(bitcell_base_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)
self.mirror = mirror
self.no_instances = True
self.location = location
#self.no_instances = True
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -69,44 +70,21 @@ class row_cap_array(bitcell_base_array):
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):
""" Add the layout pins """
row_list = self.cell.get_all_wl_names()
for row in range(1, self.row_size - 1):
for cell_row in row_list:
wl_pin = self.cell_inst[row, 0].get_pin(cell_row)
self.add_layout_pin(text=cell_row + "_{0}".format(row),
wl_names = self.cell.get_all_wl_names()
max_row = self.row_size - 2
for row in range(0, max_row):
for port in self.all_ports:
wl_pin = self.cell_inst[max_row - 1 - 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())
for row in range(1, self.row_size - 1):
for row in range(0, max_row):
for col in range(self.column_size):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:

View File

@ -662,7 +662,10 @@ class sram_1bank(design, verilog, lef):
inputs = []
outputs = []
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))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)

View File

@ -11,6 +11,7 @@ from openram.base import design
from openram.base import vector
from openram.sram_factory import factory
from openram import OPTS
from openram.tech import drc
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)
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):
# Create the enable pin that connects all write mask AND array's B pins

View File

@ -25,7 +25,7 @@ class sram_1bank_nomux_func_test(openram_test):
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
OPTS.num_sim_threads = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
from openram import characterizer

View File

@ -1,3 +1,4 @@
SHELL := /bin/bash
TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))../..)
include $(TOP_DIR)/openram.mk

View File

@ -7,8 +7,8 @@ CONDA_HOME="${CONDA_HOME:-miniconda}"
# If you want to use the latest version, just use "<tool>".
TOOLS=""
TOOLS+="klayout=0.28.3 "
TOOLS+="magic=8.3.363 "
TOOLS+="netgen=1.5.253 "
TOOLS+="magic=8.3.497 "
TOOLS+="netgen=1.5.286 "
TOOLS+="ngspice=26 "
TOOLS+="trilinos=12.12.1=1 "
TOOLS+="xyce=7.4=3"

View File

@ -65,6 +65,9 @@ all: | configs
example: $(EXAMPLE_STAMPS)
.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)
.PHONY: sky130

View File

@ -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()

View File

@ -6,7 +6,7 @@
#
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

View File

@ -6,11 +6,13 @@
#
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.base import geometry
from openram import OPTS
from .sky130_bitcell_base_array import sky130_bitcell_base_array
from math import ceil
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.
"""
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
# 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()
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
def add_modules(self):
""" Add the modules used in this design """
# Bitcell for port names only
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.strap2 = factory.create(module_type="internal", version="wlstrap_p")
self.strap3 = factory.create(module_type="internal", version="wlstrapa")
self.strap4 = factory.create(module_type="internal", version="wlstrapa_p")
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):
""" Create the module instances used in this design """
self.cell_inst = {}
self.array_layout = []
alternate_bitcell = (self.row_size) % 2
for row in range(0, self.row_size):
self.all_inst={}
self.cell_inst={}
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
for col in range(0, self.column_size):
if alternate_bitcell == 1:
row_layout.append(self.cell)
self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col),
mod=self.cell)
else:
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)
bit_block = []
pattern.append_row_to_block(bit_block, bit_row_opt1)
pattern.append_row_to_block(bit_block, bit_row_opt1a)
for row in bit_block:
row = pattern.rotate_list(row, self.column_offset * 2)
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}")
self.pattern.connect_array()

View File

@ -11,7 +11,7 @@ from openram.modules import bitcell_base_array
from openram.sram_factory import factory
from openram.tech import layer
from openram import OPTS
from openram.modules import pattern
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")
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):
"""
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"]
return strap_pins
def add_supply_pins(self):
""" Add the layout pins """
def route_supplies(self):
# Copy a vdd/gnd layout pin from every cell
super().route_supplies()
for inst in self.insts:
if "wlstrap" in inst.name:
if "VPWR" in inst.mod.pins:
self.copy_layout_pin(inst, "VPWR", "vdd")
if "VGND" in inst.mod.pins:
self.copy_layout_pin(inst, "VGND", "gnd")
if "VPWR" in inst.mod.pins:
self.copy_layout_pin(inst, "VPWR", "vdd")
if "VGND" in inst.mod.pins:
self.copy_layout_pin(inst, "VGND", "gnd")
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)
if hasattr(self, 'cell_inst'):
for col in range(self.column_size):
inst = self.cell_inst[0,col]
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()))
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)

View File

@ -7,213 +7,194 @@
from openram import debug
from openram.base import vector
from openram.base import contact
from openram import debug
from openram.base import round_to_grid
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.capped_replica_bitcell_array import capped_replica_bitcell_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
sides of a bitcell array.
"""
def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""):
super().__init__(name, rows, 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))
super().__init__(rows, cols, rbl, left_rbl, right_rbl, name)
def add_layout_pins(self):
""" Add the layout pins """
# This is how many RBLs are in all the arrays
self.rbl = rbl
# This specifies which RBL to put on the left or right by port number
# This could be an empty list
if left_rbl is not None:
self.left_rbl = left_rbl
else:
self.left_rbl = []
# This could be an empty list
if right_rbl is not None:
self.right_rbl = right_rbl
else:
self.right_rbl = []
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())
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
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')]
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
self.add_pins()
self.create_instances()
# 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
def add_modules(self):
self.replica_bitcell_array = factory.create(module_type="replica_bitcell_array",
cols=self.column_size,
rows=self.row_size,
rbl=self.rbl,
left_rbl=self.left_rbl,
right_rbl=self.right_rbl)
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):
self.bitline_names = self.replica_bitcell_array.bitline_names
self.all_bitline_names = self.replica_bitcell_array.all_bitline_names
self.rbl_bitline_names = self.replica_bitcell_array.rbl_bitline_names
self.all_rbl_bitline_names = self.replica_bitcell_array.all_rbl_bitline_names
self.bitline_pins = []
for port in self.left_rbl:
self.bitline_pins.extend(self.rbl_bitline_names[port])
self.bitline_pins.extend(self.all_bitline_names)
for port in self.right_rbl:
self.bitline_pins.extend(self.rbl_bitline_names[port])
self.add_pin_list(self.bitline_pins, "INOUT")
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()
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.02
vdd_alternate = 0
else:
connection_offset = 0.02
vdd_alternate = 1
connection_width = drc["minwidth_{}".format('m1')]
track_offset = 1
elif pin.name == 'gnd':
if gnd_alternate:
connection_offset = 0.00
gnd_alternate = 0
else:
connection_offset = 0.00
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_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 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))
def get_column_offsets(self):
return self.replica_bitcell_array.get_column_offsets()
# 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:
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)
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_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
cell_power = self.cell.analytical_power(corner, load)
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.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.
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
self.offset_all_coordinates()
self.height = self.height + self.dummy_col_insts[0].lr().y * 2
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.replica_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.
"""
self.replica_bitcell_array.graph_exclude_replica_col_bits()
def get_cell_name(self, inst_name, row, col):
"""
Gets the spice name of the target bitcell.
"""
return self.replica_bitcell_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.replica_bitcell_array_inst.name, row, col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.replica_bitcell_array.clear_exclude_bits()
for pin_name in self.bitline_pin_list:
pin_list = self.replica_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_bitcell_array_inst.mod.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

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python3
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# 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.tech import layer
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 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.
"""
def __init__(self, rows, cols, location, column_offset=0, mirror=0, name=""):
# Don't call the regular col-cap_array constructor since we don't want its constructor, just
# some of it's useful member functions
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 __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""):
super().__init__(rows, cols, column_offset=column_offset, mirror=mirror, location=location, name=name)
self.no_instances = False
def add_modules(self):
""" 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")
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
self.array_layout = []
bitline = 0
for col in range((self.column_size * 2) - 1):
row_layout = []
name="rca_{0}_{1}".format(self.location, col)
# Top/bottom cell are always dummy cells.
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl)
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell.
pins = []
if col % 4 == 0:
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 == 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.all_inst={}
self.cell_inst={}
if self.location == "top":
bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True)] \
+ [geometry.instance("01_strap_p_cent", mod=self.colend2, is_bitcell=False)]\
+ [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="MY")] \
+ [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False)]
elif self.location == "bottom":
bit_row = [geometry.instance("00_colend", mod=self.colend1, is_bitcell=True, mirror="MX")] \
+ [geometry.instance("01_strap_p_cent", mod=self.colend2, is_bitcell=False, mirror="MX")]\
+ [geometry.instance("02_colend", mod=self.colend1, is_bitcell=True, mirror="XY")] \
+ [geometry.instance("03_strap_p", mod=self.colend3, is_bitcell=False, mirror="MX")]
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)
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
return bitcell_pins
def add_supply_pins(self):
for col in range(len(self.insts)):
inst = self.cell_inst[col]
def get_strap_pins(self, row, 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:
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()))
self.place_array()
self.add_layout_pins()
if 'VNB' or 'vnb' in self.cell_inst[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 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]
self.add_boundary()
self.DRC_LVS()

View File

@ -7,27 +7,22 @@
from openram import debug
from openram.base import design
from openram.base import get_libcell_size
from openram.tech import layer, GDS
from openram.tech import cell_properties as props
class sky130_corner(design):
def __init__(self, location, name=""):
super().__init__(name)
if location == "ul":
self.name = "sky130_fd_bd_sram__sram_sp_corner"
cell_name = "sky130_fd_bd_sram__sram_sp_corner"
elif location == "ur":
self.name = "sky130_fd_bd_sram__sram_sp_cornerb"
cell_name = "sky130_fd_bd_sram__sram_sp_cornerb"
elif location == "ll":
self.name = "sky130_fd_bd_sram__sram_sp_cornera"
cell_name = "sky130_fd_bd_sram__sram_sp_cornera"
elif location == "lr":
self.name = "sky130_fd_bd_sram__sram_sp_cornera"
cell_name = "sky130_fd_bd_sram__sram_sp_cornera"
else:
debug.error("Invalid sky130_corner location", -1)
design.__init__(self, name=self.name)
(self.width, self.height) = get_libcell_size(self.name,
GDS["unit"],
layer["mem"])
# pin_map = get_libcell_pins(pin_names, self.name, GDS["unit"])
super().__init__(name=name, cell_name=cell_name, prop=props.col_cap_1port_strap_power)
self.no_instances = True

View File

@ -10,186 +10,60 @@ from openram.sram_factory import factory
from openram.tech import layer
from openram import OPTS
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.
"""
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.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):
""" Add the modules used in this design """
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.strap2 = factory.create(module_type="internal", version="wlstrap_p")
self.strap3 = factory.create(module_type="internal", version="wlstrapa")
self.strap4 = factory.create(module_type="internal", version="wlstrapa_p")
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")
self.cell = factory.create(module_type=OPTS.bitcell, version="opt1")
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
self.array_layout = []
alternate_bitcell = (self.row_size + 1) % 2
for row in range(0, self.row_size):
# 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.all_inst={}
self.cell_inst={}
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 = []
alternate_strap = (self.row_size + 1) % 2
for col in range(0, self.column_size):
if alternate_bitcell == 1:
row_layout.append(self.dummy_cell)
self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col),
mod=self.dummy_cell)
else:
row_layout.append(self.dummy_cell2)
self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col),
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
bit_block = []
if(self.row_offset % 2 == 0):
next_row = 0
else:
next_row = 1
for i in range(self.row_size):
if next_row == 0:
pattern.append_row_to_block(bit_block, bit_row_opt1)
next_row = 1
else:
alternate_bitcell = 0
self.array_layout.append(row_layout)
pattern.append_row_to_block(bit_block, bit_row_opt1a)
next_row = 0
def add_pins(self):
# bitline pins are not added because they are floating
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")
for row in bit_block:
row = pattern.rotate_list(row, self.column_offset * 2)
def add_layout_pins(self):
""" Add the layout pins """
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()
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}")
self.pattern.connect_array()

View File

@ -13,7 +13,7 @@ from openram.tech import drc
from openram.tech import array_row_multiple
from openram.tech import array_col_multiple
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
@ -27,389 +27,6 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar
bitcell (Bl/BR disconnected).
"""
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
self.all_ports = list(range(total_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")
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)

View File

@ -11,9 +11,9 @@ from openram.sram_factory import factory
from openram.tech import layer
from openram import OPTS
from .sky130_bitcell_base_array import sky130_bitcell_base_array
class sky130_replica_column(sky130_bitcell_base_array):
from openram.modules import pattern
from openram.modules import replica_column
class sky130_replica_column(replica_column, sky130_bitcell_base_array):
"""
Generate a replica bitline column for the replica 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):
# Used for pin names and properties
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")
super().__init__(name=name, rows=rows, rbl=rbl, replica_bit=replica_bit, column_offset=column_offset)
def add_modules(self):
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.dummy_cell = factory.create(module_type="dummy_bitcell_1port", version="opt1")
self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1")
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.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1a")
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):
self.cell_inst = {}
self.array_layout = []
alternate_bitcell = (self.rows + 1) % 2
""" Create the module instances used in this design """
self.all_inst={}
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):
row_layout = []
name="rbc_{0}".format(row)
# Top/bottom cell are always dummy cells.
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl)
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell.
if (row > self.left_rbl and row < self.total_size - 1 or row == self.replica_bit):
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
# Regular array cells are replica cells
# Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell.
# All other cells are dummies
if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end):
if current_row % 2 == 1:
pattern.append_row_to_block(bit_block, replica_row_opt1)
else:
row_layout.append(self.replica_cell2)
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
pattern.append_row_to_block(bit_block, replica_row_opt1a)
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)

View File

@ -5,140 +5,107 @@
# All rights reserved.
#
from openram.base import geometry
from openram.sram_factory import factory
from openram import OPTS
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(sky130_bitcell_base_array):
class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array):
"""
Generate a dummy row/column for the replica array.
"""
def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
# Don't call the regular col-cap_array constructor since we don't want its constructor, just
# 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
def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""):
super().__init__(rows, cols, column_offset=column_offset, location=location, 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 """
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()
self.location = location
def add_modules(self):
""" 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.bottom_corner =factory.create(module_type="corner", location="ll")
self.rowend1 = factory.create(module_type="row_cap", version="rowend_replica")
self.rowend2 = factory.create(module_type="row_cap", version="rowenda_replica")
#self.rowend1 = factory.create(module_type="row_cap", version="rowend_replica")
#self.rowend2 = factory.create(module_type="row_cap", version="rowenda_replica")
else:
self.top_corner = factory.create(module_type="corner", location="ur")
self.bottom_corner = factory.create(module_type="corner", location="lr")
self.rowend1 = factory.create(module_type="row_cap", version="rowend")
self.rowend2 = factory.create(module_type="row_cap", version="rowenda")
#self.rowend1 = factory.create(module_type="row_cap", version="rowend")
#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")
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
self.array_layout = []
alternate_bitcell = (self.rows + 1) % 2
for row in range(self.rows + 2):
row_layout = []
name="rca_{0}".format(row)
# Top/bottom cell are always dummy cells.
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl)
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell.
self.all_inst={}
self.cell_inst={}
bit_block = []
if self.location == "left":
top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False, mirror="MY")
bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="XY")
rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True, mirror="XY")
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):
if alternate_bitcell == 0:
row_layout.append(self.rowend1)
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])
pattern.append_row_to_block(bit_block, [top_corner])
for row in range(1, self.row_size-1):
if row % 2 == 1:
pattern.append_row_to_block(bit_block, [rowend])
else:
if row % 2 ==0:
inst.place(offset=[xoffset, yoffset + inst.height], mirror="MX")
else:
inst.place(offset=[xoffset, yoffset])
yoffset += inst.height
pattern.append_row_to_block(bit_block, [rowenda])
pattern.append_row_to_block(bit_block, [bottom_corner])
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}")
self.pattern.connect_array_raw()
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):
for row in range(self.rows + 2):
bitcell_pins = []
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:
self.add_pin("wl_{}_{}".format(port, row), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
def add_layout_pins(self):
""" Add the layout pins """
for row in range(0, self.rows + 1):
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())
self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
def create_layout(self):
# Add vdd/gnd via stacks
for row in range(1, self.rows):
inst = self.cell_inst[row]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.copy_layout_pin(inst, pin_name)
self.place_array()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()

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__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",
"sky130_fd_bd_sram__openram_sp_cell_opt1_replica",