OpenRAM/compiler/bitcells/bitcell_base.py

179 lines
6.7 KiB
Python

# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 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.
#
import debug
import design
import utils
from globals import OPTS
import logical_effort
from tech import GDS, parameter, drc, layer
class bitcell_base(design.design):
"""
Base bitcell parameters to be over-riden.
"""
cell_size_layer = "boundary"
def __init__(self, name, cell_name, hard_cell=True):
design.design.__init__(self, name, cell_name)
if hard_cell:
(self.width, self.height) = utils.get_libcell_size(cell_name,
GDS["unit"],
layer[self.cell_size_layer])
self.pin_map = utils.get_libcell_pins(self.pin_names,
cell_name,
GDS["unit"])
self.add_pin_types(self.type_list)
def get_stage_effort(self, load):
parasitic_delay = 1
# This accounts for bitline being drained
# thought the access TX and internal node
size = 0.5
# Assumes always a minimum sizes inverter.
# Could be specified in the tech.py file.
cin = 3
# min size NMOS gate load
read_port_load = 0.5
return logical_effort.logical_effort('bitline',
size,
cin,
load + read_port_load,
parasitic_delay,
False)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
# FIXME
dynamic = 0
total_power = self.return_power(dynamic, leakage)
return total_power
def input_load(self):
""" Return the relative capacitance of the access transistor gates """
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell.
# Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
# This is a handmade cell so the value must be entered
# in the tech.py file or estimated.
# Calculated in the tech file by summing the widths of all
# the related gates and dividing by the minimum width.
# FIXME: sizing is not accurate with the handmade cell.
# Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def get_storage_net_names(self):
"""
Returns names of storage nodes in bitcell in
[non-inverting, inverting] format.
"""
# Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
fmt_str = "Storage nodes={} not found in spice file."
debug.info(1, fmt_str.format(self.storage_nets))
return None
def get_storage_net_offset(self):
"""
Gets the location of the storage net labels to add top level
labels for pex simulation.
"""
# If we generated the bitcell, we already know where Q and Q_bar are
if OPTS.bitcell is not "pbitcell":
self.storage_net_offsets = []
for i in range(len(self.get_storage_net_names())):
for text in self.gds.getTexts(layer["m1"]):
if self.storage_nets[i] == text.textString.rstrip('\x00'):
self.storage_net_offsets.append(text.coordinates[0])
for i in range(len(self.storage_net_offsets)):
self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]])
return(self.storage_net_offsets)
def get_bitline_offset(self):
bl_names = self.get_all_bl_names()
br_names = self.get_all_br_names()
found_bl = []
found_br = []
self.bl_offsets = []
self.br_offsets = []
for i in range(len(bl_names)):
for text in self.gds.getTexts(layer["m2"]):
if not bl_names[i] in found_bl:
if bl_names[i] == text.textString.rstrip('\x00'):
self.bl_offsets.append(text.coordinates[0])
found_bl.append(bl_names[i])
continue
for i in range(len(br_names)):
for text in self.gds.getTexts(layer["m2"]):
if not br_names[i] in found_br:
if br_names[i] == text.textString.rstrip('\x00'):
self.br_offsets.append(text.coordinates[0])
found_br.append(br_names[i])
continue
for i in range(len(self.bl_offsets)):
self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]])
for i in range(len(self.br_offsets)):
self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]])
return(self.bl_offsets, self.br_offsets, found_bl, found_br)
def get_normalized_storage_nets_offset(self):
"""
Convert storage net offset to be relative to the bottom left corner
of the bitcell. This is useful for making sense of offsets outside
of the bitcell.
"""
if OPTS.bitcell is not "pbitcell":
normalized_storage_net_offset = self.get_storage_net_offset()
else:
net_offset = self.get_storage_net_offset()
Q_x = net_offset[0][0] - self.leftmost_xpos
Q_y = net_offset[0][1] - self.botmost_ypos
Q_bar_x = net_offset[1][0] - self.leftmost_xpos
Q_bar_y = net_offset[1][1] - self.botmost_ypos
normalized_storage_net_offset = [[Q_x,Q_y],[Q_bar_x,Q_bar_y]]
return normalized_storage_net_offset
def get_normalized_bitline_offset(self):
return self.get_bitline_offset()
def build_graph(self, graph, inst_name, port_nets):
"""
By default, bitcells won't be part of the graph.
"""
return