2020-01-30 20:44:24 +01:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
|
|
|
|
# Copyright (c) 2016-2020 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.
|
|
|
|
|
#
|
2020-02-12 15:03:47 +01:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
class _cell:
|
2020-11-21 17:05:49 +01:00
|
|
|
def __init__(self, port_order, port_types, port_map=None, body_bias=None, hard_cell=True, boundary_layer="boundary"):
|
|
|
|
|
|
|
|
|
|
# Some cells may have body bias (well taps) exposed as ports
|
|
|
|
|
self._body_bias = body_bias
|
2020-11-16 20:04:03 +01:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
# Specifies if this is a hard (i.e. GDS) cell
|
|
|
|
|
self._hard_cell = hard_cell
|
2020-11-14 16:15:27 +01:00
|
|
|
self._boundary_layer = boundary_layer
|
2020-11-16 20:04:03 +01:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
# Specifies the port directions
|
2020-11-14 16:15:27 +01:00
|
|
|
self._port_types_map = {x: y for (x, y) in zip(port_order, port_types)}
|
2020-11-16 20:04:03 +01:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
# Specifies a map from OpenRAM names to cell names
|
|
|
|
|
# by default it is 1:1
|
|
|
|
|
if not port_map:
|
2020-11-16 20:04:03 +01:00
|
|
|
self._port_map = {x: x for x in port_order}
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-11-14 16:15:27 +01:00
|
|
|
# Update mapping of names
|
2020-11-17 20:12:59 +01:00
|
|
|
self._original_port_order = port_order
|
2020-11-14 16:15:27 +01:00
|
|
|
self._port_order = port_order
|
2020-11-17 20:12:59 +01:00
|
|
|
|
|
|
|
|
# Create an index array
|
|
|
|
|
self._port_indices = [self._port_order.index(x) for x in self._original_port_order]
|
2020-11-14 17:08:42 +01:00
|
|
|
|
2020-11-14 16:15:27 +01:00
|
|
|
# Update ordered name list
|
2020-11-16 20:04:03 +01:00
|
|
|
self._port_names = [self._port_map[x] for x in self._port_order]
|
2020-11-14 17:08:42 +01:00
|
|
|
|
2020-11-14 16:15:27 +01:00
|
|
|
# Update ordered type list
|
2020-11-16 20:04:03 +01:00
|
|
|
self._port_types = [self._port_types_map[x] for x in self._port_order]
|
2020-11-14 16:15:27 +01:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
@property
|
|
|
|
|
def hard_cell(self):
|
|
|
|
|
return self._hard_cell
|
2020-11-14 16:15:27 +01:00
|
|
|
|
|
|
|
|
@property
|
2020-11-14 00:55:55 +01:00
|
|
|
def port_names(self):
|
2020-11-14 16:15:27 +01:00
|
|
|
return self._port_names
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
@property
|
|
|
|
|
def port_order(self):
|
|
|
|
|
return self._port_order
|
|
|
|
|
|
|
|
|
|
@port_order.setter
|
2020-11-21 17:05:49 +01:00
|
|
|
def port_order(self, port_order):
|
|
|
|
|
# If we are going to redefine more ports (i.e. well biases) don't init stuff
|
|
|
|
|
old_port_len = len(self._port_order)
|
|
|
|
|
if old_port_len == len(port_order):
|
|
|
|
|
self._port_order = port_order
|
|
|
|
|
# Update ordered name list in the new order
|
|
|
|
|
self._port_names = [self._port_map[x] for x in self._port_order]
|
|
|
|
|
# Update ordered type list in the new order
|
|
|
|
|
self._port_types = [self._port_types_map[x] for x in self._port_order]
|
|
|
|
|
# Update the index array
|
|
|
|
|
self._port_indices = [self._port_order.index(x) for x in self._original_port_order]
|
|
|
|
|
else:
|
|
|
|
|
# Do the default constructor again except for types stuff which hasn't been set yet
|
|
|
|
|
self._port_order = port_order
|
|
|
|
|
self._original_port_order = self._port_order
|
|
|
|
|
self._port_map = {x: x for x in self._port_order}
|
|
|
|
|
self._port_indices = [self._port_order.index(x) for x in self._original_port_order]
|
|
|
|
|
self._port_names = [self._port_map[x] for x in self._port_order]
|
2020-11-14 17:08:42 +01:00
|
|
|
|
2020-11-17 20:12:59 +01:00
|
|
|
@property
|
|
|
|
|
def port_indices(self):
|
|
|
|
|
return self._port_indices
|
|
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
@property
|
|
|
|
|
def port_map(self):
|
|
|
|
|
return self._port_map
|
|
|
|
|
|
|
|
|
|
@port_map.setter
|
2020-11-21 17:05:49 +01:00
|
|
|
def port_map(self, port_map):
|
|
|
|
|
self._port_map = port_map
|
2020-11-14 17:08:42 +01:00
|
|
|
# Update ordered name list to use the new names
|
2020-11-16 22:42:42 +01:00
|
|
|
self._port_names = [self.port_map[x] for x in self._port_order]
|
2020-11-14 17:08:42 +01:00
|
|
|
|
2020-11-21 17:05:49 +01:00
|
|
|
@property
|
|
|
|
|
def body_bias(self):
|
|
|
|
|
return self._body_bias
|
|
|
|
|
|
|
|
|
|
@body_bias.setter
|
|
|
|
|
def body_bias(self, body_bias):
|
|
|
|
|
# It is assumed it is [nwell, pwell]
|
|
|
|
|
self._body_bias = body_bias
|
|
|
|
|
self._port_map['vnb'] = body_bias[0]
|
|
|
|
|
self._port_types['vnb'] = "POWER"
|
|
|
|
|
self._port_map['vpb'] = body_bias[1]
|
|
|
|
|
self._port_types['vpb'] = "GROUND"
|
|
|
|
|
|
2020-11-14 16:15:27 +01:00
|
|
|
@property
|
2020-11-14 00:55:55 +01:00
|
|
|
def port_types(self):
|
2020-11-14 16:15:27 +01:00
|
|
|
return self._port_types
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-11-21 17:05:49 +01:00
|
|
|
@port_types.setter
|
|
|
|
|
def port_types(self, port_types):
|
|
|
|
|
self._port_types = port_types
|
|
|
|
|
# Specifies the port directions
|
|
|
|
|
self._port_types_map = {x: y for (x, y) in zip(self._port_order, self._port_types)}
|
|
|
|
|
# Update ordered type list
|
|
|
|
|
self._port_types = [self._port_types_map[x] for x in self._port_order]
|
|
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
@property
|
|
|
|
|
def boundary_layer(self):
|
|
|
|
|
return self._boundary_layer
|
2020-11-14 17:08:42 +01:00
|
|
|
|
|
|
|
|
@boundary_layer.setter
|
|
|
|
|
def boundary_layer(self, x):
|
|
|
|
|
self._boundary_layer = x
|
|
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-02-12 15:03:47 +01:00
|
|
|
class _pins:
|
|
|
|
|
def __init__(self, pin_dict):
|
|
|
|
|
# make the pins elements of the class to allow "." access.
|
2020-11-13 19:07:40 +01:00
|
|
|
# For example: props.bitcell.cell_1port.pin.bl = "foobar"
|
2020-10-26 21:13:38 +01:00
|
|
|
for k, v in pin_dict.items():
|
2020-02-12 15:03:47 +01:00
|
|
|
self.__dict__[k] = v
|
|
|
|
|
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-02-12 05:09:40 +01:00
|
|
|
class _mirror_axis:
|
|
|
|
|
def __init__(self, x, y):
|
|
|
|
|
self.x = x
|
|
|
|
|
self.y = y
|
|
|
|
|
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-28 17:54:15 +01:00
|
|
|
class _ptx:
|
|
|
|
|
def __init__(self, model_is_subckt, bin_spice_models):
|
|
|
|
|
self.model_is_subckt = model_is_subckt
|
|
|
|
|
self.bin_spice_models = bin_spice_models
|
|
|
|
|
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-28 17:54:15 +01:00
|
|
|
class _pgate:
|
|
|
|
|
def __init__(self, add_implants):
|
|
|
|
|
self.add_implants = add_implants
|
|
|
|
|
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
class _bitcell(_cell):
|
|
|
|
|
def __init__(self, port_order, port_types, port_map=None, storage_nets=["Q", "Q_bar"], mirror=None, end_caps=False):
|
|
|
|
|
super().__init__(port_order, port_types, port_map)
|
2020-02-12 15:03:47 +01:00
|
|
|
|
2020-11-14 02:29:20 +01:00
|
|
|
self.end_caps = end_caps
|
2020-11-14 00:55:55 +01:00
|
|
|
|
|
|
|
|
if not mirror:
|
2020-11-14 02:29:20 +01:00
|
|
|
self.mirror = _mirror_axis(True, False)
|
2020-11-14 00:55:55 +01:00
|
|
|
else:
|
2020-11-14 02:29:20 +01:00
|
|
|
self.mirror = mirror
|
2020-02-12 15:03:47 +01:00
|
|
|
|
2020-11-14 02:29:20 +01:00
|
|
|
self.storage_nets = storage_nets
|
2020-01-30 20:44:24 +01:00
|
|
|
|
2020-11-14 01:23:27 +01:00
|
|
|
|
2020-02-12 05:09:40 +01:00
|
|
|
class cell_properties():
|
2020-01-30 20:44:24 +01:00
|
|
|
"""
|
2020-02-12 15:03:47 +01:00
|
|
|
This contains meta information about the custom designed cells. For
|
|
|
|
|
instance, pin names, or the axis on which they need to be mirrored. These
|
|
|
|
|
can be overriden in the tech.py file.
|
2020-01-30 20:44:24 +01:00
|
|
|
"""
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.names = {}
|
2020-02-12 05:09:40 +01:00
|
|
|
|
2020-11-13 19:07:40 +01:00
|
|
|
self.names["bitcell_1port"] = "cell_1rw"
|
|
|
|
|
self.names["bitcell_2port"] = "cell_2rw"
|
|
|
|
|
self.names["dummy_bitcell_1port"] = "dummy_cell_1rw"
|
|
|
|
|
self.names["dummy_bitcell_2port"] = "dummy_cell_2rw"
|
|
|
|
|
self.names["replica_bitcell_1port"] = "replica_cell_1rw"
|
|
|
|
|
self.names["replica_bitcell_2port"] = "replica_cell_2rw"
|
|
|
|
|
self.names["col_cap_bitcell_1port"] = "col_cap_cell_1rw"
|
|
|
|
|
self.names["col_cap_bitcell_2port"] = "col_cap_cell_2rw"
|
|
|
|
|
self.names["row_cap_bitcell_1port"] = "row_cap_cell_1rw"
|
|
|
|
|
self.names["row_cap_bitcell_2port"] = "row_cap_cell_2rw"
|
2020-10-28 17:54:15 +01:00
|
|
|
|
|
|
|
|
self._ptx = _ptx(model_is_subckt=False,
|
|
|
|
|
bin_spice_models=False)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-28 17:54:15 +01:00
|
|
|
self._pgate = _pgate(add_implants=False)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
self._inv_dec = _cell(["A", "Z", "vdd", "gnd"],
|
|
|
|
|
["INPUT", "OUTPUT", "POWER", "GROUND"])
|
|
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
self._nand2_dec = _cell(["A", "B", "Z", "vdd", "gnd"],
|
|
|
|
|
["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
|
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
self._nand3_dec = _cell(["A", "B", "C", "Z", "vdd", "gnd"],
|
2020-11-14 00:55:55 +01:00
|
|
|
["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
|
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
self._nand4_dec = _cell(["A", "B", "C", "D", "Z", "vdd", "gnd"],
|
2020-11-14 00:55:55 +01:00
|
|
|
["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
|
|
|
|
|
2020-11-14 01:23:27 +01:00
|
|
|
self._dff = _cell(["D", "Q", "clk", "vdd", "gnd"],
|
|
|
|
|
["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"])
|
|
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
self._write_driver = _cell(['din', 'bl', 'br', 'en', 'vdd', 'gnd'],
|
|
|
|
|
["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"])
|
2020-09-23 03:33:03 +02:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
self._sense_amp = _cell(['bl', 'br', 'dout', 'en', 'vdd', 'gnd'],
|
|
|
|
|
["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"])
|
2020-02-17 14:25:00 +01:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
self._bitcell_1port = _bitcell(["bl", "br", "wl", "vdd", "gnd"],
|
2020-11-14 01:41:02 +01:00
|
|
|
["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"])
|
2020-09-23 03:33:03 +02:00
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
self._bitcell_2port = _bitcell(["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"],
|
|
|
|
|
["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"])
|
2020-02-12 05:09:40 +01:00
|
|
|
|
2020-11-22 17:24:47 +01:00
|
|
|
self._col_cap_1port = _bitcell(["bl", "br", "vdd"],
|
|
|
|
|
["OUTPUT", "OUTPUT", "POWER"])
|
|
|
|
|
|
2020-11-23 15:55:47 +01:00
|
|
|
self._col_cap_1port_bitcell = _cell(["bl", "br", "vdd", "gnd"],
|
|
|
|
|
["OUTPUT", "OUTPUT", "POWER", "GROUND"])
|
|
|
|
|
|
|
|
|
|
self._col_cap_1port_strap = _cell(["vdd", "gnd"],
|
|
|
|
|
["POWER", "GROUND"])
|
|
|
|
|
|
|
|
|
|
|
2020-11-22 17:24:47 +01:00
|
|
|
self._row_cap_1port = _bitcell(["wl", "gnd"],
|
|
|
|
|
["INPUT", "POWER", "GROUND"])
|
|
|
|
|
|
2020-11-16 22:42:42 +01:00
|
|
|
self._col_cap_2port = _bitcell(["bl0", "br0", "bl1", "br1", "vdd"],
|
|
|
|
|
["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "POWER"])
|
|
|
|
|
|
|
|
|
|
self._row_cap_2port = _bitcell(["wl0", "wl1", "gnd"],
|
|
|
|
|
["INPUT", "INPUT", "POWER", "GROUND"])
|
2020-11-22 17:24:47 +01:00
|
|
|
|
2020-10-28 17:54:15 +01:00
|
|
|
@property
|
|
|
|
|
def ptx(self):
|
|
|
|
|
return self._ptx
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-28 17:54:15 +01:00
|
|
|
@property
|
|
|
|
|
def pgate(self):
|
|
|
|
|
return self._pgate
|
|
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
@property
|
|
|
|
|
def inv_dec(self):
|
|
|
|
|
return self._inv_dec
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def nand2_dec(self):
|
|
|
|
|
return self._nand2_dec
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def nand3_dec(self):
|
|
|
|
|
return self._nand3_dec
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def nand4_dec(self):
|
|
|
|
|
return self._nand4_dec
|
|
|
|
|
|
2020-01-30 20:44:24 +01:00
|
|
|
@property
|
|
|
|
|
def dff(self):
|
|
|
|
|
return self._dff
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-02-17 14:25:00 +01:00
|
|
|
@property
|
|
|
|
|
def write_driver(self):
|
|
|
|
|
return self._write_driver
|
2020-02-17 14:27:35 +01:00
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def sense_amp(self):
|
|
|
|
|
return self._sense_amp
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-09-23 03:33:03 +02:00
|
|
|
@property
|
2020-11-14 00:55:55 +01:00
|
|
|
def bitcell_1port(self):
|
|
|
|
|
return self._bitcell_1port
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def bitcell_2port(self):
|
|
|
|
|
return self._bitcell_2port
|
2020-09-23 03:33:03 +02:00
|
|
|
|
2020-11-22 17:24:47 +01:00
|
|
|
@property
|
|
|
|
|
def col_cap_1port(self):
|
|
|
|
|
return self._col_cap_1port
|
|
|
|
|
|
2020-11-23 15:55:47 +01:00
|
|
|
@property
|
|
|
|
|
def col_cap_1port_bitcell(self):
|
|
|
|
|
return self._col_cap_1port_bitcell
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def col_cap_1port_strap(self):
|
|
|
|
|
return self._col_cap_1port_strap
|
|
|
|
|
|
2020-11-22 17:24:47 +01:00
|
|
|
@property
|
|
|
|
|
def row_cap_1port(self):
|
|
|
|
|
return self._row_cap_1port
|
|
|
|
|
|
2020-11-16 22:42:42 +01:00
|
|
|
@property
|
|
|
|
|
def col_cap_2port(self):
|
|
|
|
|
return self._col_cap_2port
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def row_cap_2port(self):
|
|
|
|
|
return self._row_cap_2port
|
|
|
|
|
|