Merge branch 'singleport_refactor' into array_gen

This commit is contained in:
Jesse Cirimelli-Low
2025-02-24 23:26:28 -08:00
35 changed files with 1133 additions and 2828 deletions
+1 -1
View File
@@ -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 *
+23 -13
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."""
+12 -32
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):
"""
+113 -96
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
+18 -18
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)
+24 -53
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()
-116
View File
@@ -1,116 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2024 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from openram.sram_factory import factory
from openram.tech import drc, spice
from openram import OPTS
from .bitcell_base_array import bitcell_base_array
class bitcell_array(bitcell_base_array):
"""
Creates a rows x cols array of memory cells. Assumes bit-lines
and word line is connected by abutment.
Connects the word lines and bit lines.
"""
def __init__(self, cols, rows, name, column_offset=0):
super().__init__(cols, rows, name, column_offset)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
# We don't offset this because we need to align
# the replica bitcell in the control logic
# self.offset_all_coordinates()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place_array("bit_r{0}_c{1}")
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
""" Add the modules used in this design """
self.cell = factory.create(module_type=OPTS.bitcell)
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
for col in range(self.column_size):
for row in range(self.row_size):
name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.cell)
self.connect_inst(self.get_bitcell_pins(col, row))
def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW."""
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
# Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(corner, load)
# Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
cell_power.leakage * self.column_size * self.row_size)
return total_power
def gen_wl_wire(self):
if OPTS.netlist_only:
width = 0
else:
width = self.width
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1"))
wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
return wl_wire
def gen_bl_wire(self):
if OPTS.netlist_only:
height = 0
else:
height = self.height
bl_pos = 0
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1"))
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
return bl_wire
def get_wordline_cin(self):
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
bitcell_wl_cin = self.cell.get_wl_cin()
total_cin = bitcell_wl_cin * self.column_size
return total_cin
def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target"""
# Function is not robust with column mux configurations
for row in range(self.row_size):
for col in range(self.column_size):
if row == targ_row and col == targ_col:
continue
self.graph_inst_exclude.add(self.cell_inst[row, col])
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
return inst_name + "{}x".format(OPTS.hier_seperator) + self.cell_inst[row, col].name, self.cell_inst[row, col]
+223
View File
@@ -0,0 +1,223 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
# All rights reserved.
#
from openram import debug
from openram.base.geometry import instance,geometry
from typing import List
from typing import Optional
from openram.base import design
from openram.globals import OPTS
from math import ceil, floor
from copy import deepcopy
class pattern():
"""
This class is used to desribe the internals of a bitcell array. It describes
instance modules, rotation, and ordering
"""
block = List[List[instance]]
def __init__(self,
parent_design: design,
name:str,
core_block:block,
num_rows:int,
num_cols:int,
name_template,
num_cores_x: Optional[int] = 0,
num_cores_y: Optional[int] = 0,
cores_per_x_block: int = 1,
cores_per_y_block: int = 1,
x_block: Optional[block] = None,
y_block: Optional[block] = None,
xy_block: Optional[block] = None,
initial_x_block:bool = False,
initial_y_block:bool = False,
final_x_block:bool = False,
final_y_block:bool = False,
):
"""
a "block" is a 2d list of instances
core_block defines the main block that is tiled
num_core defines the number of times the core block is to be tiled
(i.e. bitcells in dimension / bitcells in core_block)
x_block defines a block that is inserted to the right of the core_block
y_block defines a block that is inserted below the core block
xy_block defines a block that is a inserted where the x_block and y_block intercept
the initial and final booleans determine whether the pattern begins and/or ends with x/y blocks
"""
self.parent_design = parent_design
self.name = name
self.core_block = core_block
self.num_rows = num_rows
self.num_cols = num_cols
self.name_template = name_template
self.num_cores_x = num_cores_x
self.num_cores_y = num_cores_y
if num_cores_x == 0:
self.num_cores_x = ceil(num_cols/len(core_block[0]))
if num_cores_y == 0:
self.num_cores_y = ceil(num_rows/len(core_block))
self.cores_per_x_block = cores_per_x_block
self.cores_per_y_block = cores_per_y_block
self.x_block = x_block
self.y_block = y_block
self.xy_block = xy_block
self.initial_x_block = initial_x_block
self.initial_y_block = initial_y_block
self.final_x_block = final_x_block
self.final_y_block = final_y_block
self.bits_per_row = ceil(self.num_rows/self.num_cores_x)
self.bits_per_col = ceil(self.num_cols/self.num_cores_y)
self.bit_rows = []
self.bit_cols = []
self.parent_design.all_inst = {}
if not OPTS.netlist_only:
self.verify_interblock_dimensions()
def compute_and_verify_intrablock_dimensions(self, block: block) -> List[int]:
for row in block:
row_height = row[0].height
for inst in row:
debug.check(row_height == inst.height, "intrablock instances within the same row are different heights")
for y in range(len(block[0])):
debug.check(all([row[y].width for row in block]), "intrablock instances within the same column are different widths")
block_width = sum([instance.width for instance in block[0]])
block_height = sum([row[0].height for row in block])
return [block_width, block_height]
def verify_interblock_dimensions(self) -> None:
"""
Ensure the individual blocks are valid and interblock dimensions are valid
"""
debug.check(len(self.core_block) >= 1, "invalid core_block dimension: core_block rows must be >=1")
debug.check(len(self.core_block[0]) >= 1, "invalid core_block dimension: core_block cols must be >=1")
if self.x_block and self.y_block:
debug.check(self.xy_block is not None, "must have xy_block if both x_block and y_block are provided")
(self.core_block_width, self.core_block_height) = self.compute_and_verify_intrablock_dimensions(self.core_block)
if self.x_block:
(self.x_block_width, self.x_block_height) = self.compute_and_verify_intrablock_dimensions(self.x_block)
if self.y_block:
(self.y_block_width, self.y_block_height) = self.compute_and_verify_intrablock_dimensions(self.y_block)
if self.xy_block:
(self.xy_block_width, self.xy_block_height) = self.compute_and_verify_intrablock_dimensions(self.xy_block)
if(self.x_block):
debug.check(self.core_block_width * self.cores_per_x_block == self.x_block_width, "core_block does not align with x_block")
if(self.y_block):
debug.check(self.core_block_height * self.cores_per_y_block == self.y_block_height, "core_block does not aligns with y_block")
if(self.xy_block):
debug.check(self.xy_block_height == self.x_block_height, "xy_block does not align with x_block")
debug.check(self.xy_block_width == self.y_block_width, "xy_block does not align with y_block")
def connect_block(self, block: block, col: int, row: int):
for dr in range(len(block)):
row_done = False
for dc in range(len(block[0])):
if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.num_cols) <= self.num_rows):
inst = block[dr][dc]
if(len(self.bit_rows) <= col + dc):
self.bit_rows.append(0)
if(len(self.bit_cols) <= row + dr):
self.bit_cols.append(0)
if(row_done or self.bit_cols[row+dr] >= self.num_cols):
row_done = True
continue
if((self.bit_rows[col+dc] < self.num_rows) and (self.bit_cols[row+dr] < self.num_cols)):
if(inst.is_bitcell):
self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc))
self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]]
self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col+dc], self.bit_cols[row+dr]))
self.bit_rows[col+dc] += 1
self.bit_cols[row+dr] += 1
else:
self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc))
self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col+dc], self.bit_cols[row+dr]))
else:
row_done = True
def connect_array(self) -> None:
#debug_array = [[None]*12 for _ in range(6)]
row = 0
col = 0
for i in range(self.num_cores_y):
for j in range (self.num_cores_x):
self.connect_block(self.core_block, col, row)
col += len(self.core_block[0])
col = 0
row += len(self.core_block)
def connect_array_raw(self) -> None:
for row in range(self.num_rows):
for col in range(self.num_cols):
inst = self.core_block[row][col]
if(len(self.bit_rows) <= col):
self.bit_rows.append(0)
if(len(self.bit_cols) <= row):
self.bit_cols.append(0)
if(inst.is_bitcell):
self.parent_design.cell_inst[self.bit_rows[col], self.bit_cols[row]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row, col))
self.parent_design.all_inst[row, col] = self.parent_design.cell_inst[self.bit_rows[col], self.bit_cols[row]]
self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col], self.bit_cols[row]))
self.bit_rows[col] += 1
self.bit_cols[row] += 1
else:
self.parent_design.all_inst[row, col] = self.parent_design.add_existing_inst(inst,self.name_template.format(row, col))
self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col], self.bit_cols[row]))
def print_bit_blocck():
return
def place_inst(self, inst, offset) -> None:
x = offset[0]
y = offset[1]
if "X" in inst.mirror:
y += inst.height
if "Y" in inst.mirror:
x += inst.width
#print('placing inst {} at {}'.format(inst, offset))
inst.place((x, y), inst.mirror, inst.rotate)
def place_array(self):
(self.row_max, self.col_max) = list(self.parent_design.all_inst.keys())[-1]
y = 0
for row in range(self.row_max+1):
x = 0
for col in range(self.col_max+1):
inst = self.parent_design.all_inst[self.row_max - row, col]
self.place_inst(inst, (x, y))
x += inst.width
y += inst.height
self.parent_design.width = max([x.rx() for x in self.parent_design.insts])
self.parent_design.height = max([x.uy() for x in self.parent_design.insts])
def append_row_to_block(block, row):
block.append(row)
def append_block_under_block(base_block, under_block):
base_block = base_block + under_block
def append_block_right_block(base_block, right_block):
for row in base_block:
row = row + right_block
def rotate_list(lst, n):
# Loop through the range of n positions to rotate
for i in range(n):
# Remove the last element of the list and insert it at the beginning
lst.insert(0, lst.pop())
# Return the rotated list
return lst
+7 -5
View File
@@ -156,13 +156,15 @@ class pgate(design):
return via
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:
+49 -43
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
+28 -98
View File
@@ -9,7 +9,8 @@ from openram.sram_factory import factory
from openram.tech import layer_properties as layer_props
from openram 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])
+10 -32
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"]:
+4 -1
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)
+5
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