singleport bitcell array laying out

This commit is contained in:
Jesse Cirimelli-Low 2023-08-21 19:24:06 -07:00
parent 9ac894e2ef
commit 5a6c78865d
6 changed files with 94 additions and 304 deletions

View File

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

View File

@ -1,116 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 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]

View File

@ -9,7 +9,9 @@ from typing import List
from typing import Optional
from openram.base import design
from openram.globals import OPTS
from math import ceil
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
@ -68,6 +70,8 @@ class pattern():
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)
if not OPTS.netlist_only:
self.verify_interblock_dimensions()
@ -113,23 +117,39 @@ class pattern():
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.bit_cols) != self.num_rows):
if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.bit_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(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols):
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)):
print(row+dr, col+dc)
if(inst.is_bitcell):
#x_bit = sum(bit > 0 for bit in self.bit_rows)
#y_bit = sum(bit > 0 for bit in self.bit_cols)
#print(x_bit, y_bit)
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
self.parent_design.cell_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_bitcell_pins(row+dr, col+dc))
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:
self.bit_rows = []
self.bit_cols = []
#debug_array = [[None]*12 for _ in range(6)]
row = 0
col = 0
for i in range(self.num_cores_y):
@ -138,7 +158,7 @@ class pattern():
col += len(self.core_block[0])
col = 0
row += len(self.core_block)
def place_inst(self, inst, offset) -> None:
x = offset[0]
y = offset[1]
@ -148,55 +168,27 @@ class pattern():
x += inst.width
inst.place((x, y), inst.mirror, inst.rotate)
def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float, bx, by):
x_offset = 0
y_offset = 0
bounding_x = bx
bounding_y = by
for dr in range(len(block)):
for dc in range(len(block[0])):
if(self.bit_rows.count(self.num_rows) != self.num_cols and self.bit_cols.count(self.bit_cols) != self.num_rows):
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(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols):
inst = self.parent_design.cell_inst[row + dr, col +dc]
if(inst.is_bitcell):
self.bit_rows[col+dc] += 1
self.bit_cols[row+dr] += 1
self.place_inst(inst, (place_x + x_offset, place_y + y_offset))
if(place_x + x_offset + inst.width > bounding_x):
bounding_x = place_x + x_offset + inst.width
if(place_y + y_offset + inst.height > bounding_y):
bounding_y = place_y + y_offset + inst.height
x_offset += inst.width
x_offset = 0
y_offset += inst.height
return bounding_x, bounding_y
def place_array(self):
self.bit_rows = []
self.bit_cols = []
row = 0
col = 0
place_x = 0
place_y = 0
bounding_x = 0
bounding_y = 0
for i in range(self.num_cores_y):
col = 0
place_x = 0
for j in range(self.num_cores_x):
self.parent_design.width, self.parent_design.height= self.place_block(self.core_block, row, col, place_x, place_y, bounding_x, bounding_y)
if(self.bit_rows.count(self.num_rows) == self.num_cols and self.bit_cols.count(self.bit_cols) == self.num_rows):
return
(row_max, col_max) = list(self.parent_design.all_inst.keys())[-1]
y = 0
for row in range(row_max+1):
x = 0
for col in range(col_max+1):
inst = self.parent_design.all_inst[row, col]
self.place_inst(inst, (x, y))
x += inst.width
y += inst.height
place_x += self.core_block_width
col += len(self.core_block[0])
row += len(self.core_block)
place_y += self.core_block_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

View File

@ -31,7 +31,7 @@ class array_test(openram_test):
num_spare_rows = 0
num_spare_cols = 0
a = factory.create(module_type="bitcell_array", cols=8 + num_spare_cols, rows=8 + num_spare_rows)
a = factory.create(module_type="bitcell_array", cols=4 + num_spare_cols, rows=4 + num_spare_rows)
self.local_check(a)
openram.end_openram()

View File

@ -6,11 +6,12 @@
#
from openram import debug
from openram.modules import bitcell_array
from openram.modules import bitcell_array, 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):
"""
@ -38,56 +39,29 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array):
""" 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={}
bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True)] \
+ [geometry.instance("01_strap", mod=self.strap, is_bitcell=False)]\
+ [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True)] \
+ [geometry.instance("03_strap_p", mod=self.strap_p, is_bitcell=False)]
bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True)] \
+ [geometry.instance("11_strapa", mod=self.strapa, is_bitcell=False)] \
+ [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True)] \
+ [geometry.instance("13_strapa_p", mod=self.strapa_p, is_bitcell=False)]
row_layout = []
bit_block = []
pattern.append_row_to_block(bit_block, bit_row_opt1)
pattern.append_row_to_block(bit_block, bit_row_opt1a)
self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.row_size/2), name_template="bit_r{0}_c{1}")
self.pattern.connect_array()
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)

View File

@ -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,
@ -139,49 +101,28 @@ class sky130_bitcell_base_array(bitcell_base_array):
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
if row == 2: #add only 1 label per col
# 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())
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)