Merged with dev

This commit is contained in:
Hunter Nichols 2020-11-10 15:47:56 -08:00
commit 84ba5c55d1
375 changed files with 20170 additions and 15576 deletions

View File

@ -34,7 +34,7 @@ class contact(hierarchy_design.hierarchy_design):
# This will ignore the name parameter since
# we can guarantee a unique name here
super().__init__(name)
super().__init__(name, name)
debug.info(4, "create contact object {0}".format(name))
self.add_comment("layers: {0}".format(layer_stack))
@ -51,8 +51,8 @@ class contact(hierarchy_design.hierarchy_design):
# Non-preferred directions
if directions == "nonpref":
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
first_dir = "H" if tech.preferred_directions[layer_stack[0]]=="V" else "V"
second_dir = "H" if tech.preferred_directions[layer_stack[2]]=="V" else "V"
self.directions = (first_dir, second_dir)
# Preferred directions
elif directions == "pref":

View File

@ -7,13 +7,15 @@
#
from globals import OPTS
class _pins:
def __init__(self, pin_dict):
# make the pins elements of the class to allow "." access.
# For example: props.bitcell.cell_6t.pin.bl = "foobar"
for k,v in pin_dict.items():
for k, v in pin_dict.items():
self.__dict__[k] = v
class _cell:
def __init__(self, pin_dict):
pin_dict.update(self._default_power_pins())
@ -24,13 +26,27 @@ class _cell:
return self._pins
def _default_power_pins(self):
return { 'vdd' : 'vdd', 'gnd' : 'gnd' }
return {'vdd': 'vdd',
'gnd': 'gnd'}
class _mirror_axis:
def __init__(self, x, y):
self.x = x
self.y = y
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
class _pgate:
def __init__(self, add_implants):
self.add_implants = add_implants
class _bitcell:
def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r):
self.mirror = mirror
@ -42,27 +58,27 @@ class _bitcell:
def _default():
axis = _mirror_axis(True, False)
cell_s8_6t = _cell({'bl' : 'bl',
'br' : 'br',
cell_s8_6t = _cell({'bl': 'bl',
'br': 'br',
'wl': 'wl'})
cell_6t = _cell({'bl' : 'bl',
'br' : 'br',
'wl' : 'wl'})
cell_6t = _cell({'bl': 'bl',
'br': 'br',
'wl': 'wl'})
cell_1rw1r = _cell({'bl0' : 'bl0',
'br0' : 'br0',
'bl1' : 'bl1',
'br1' : 'br1',
'wl0' : 'wl0',
'wl1' : 'wl1'})
cell_1rw1r = _cell({'bl0': 'bl0',
'br0': 'br0',
'bl1': 'bl1',
'br1': 'br1',
'wl0': 'wl0',
'wl1': 'wl1'})
cell_1w1r = _cell({'bl0' : 'bl0',
'br0' : 'br0',
'bl1' : 'bl1',
'br1' : 'br1',
'wl0' : 'wl0',
'wl1' : 'wl1'})
cell_1w1r = _cell({'bl0': 'bl0',
'br0': 'br0',
'bl1': 'bl1',
'br1': 'br1',
'wl0': 'wl0',
'wl1': 'wl1'})
return _bitcell(cell_s8_6t=cell_s8_6t,
cell_6t=cell_6t,
@ -94,21 +110,25 @@ class _dff:
self.custom_type_list = custom_type_list
self.clk_pin = clk_pin
class _dff_buff:
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports
self.buf_ports = custom_buff_ports
self.add_body_contacts = add_body_contacts
class _dff_buff_array:
def __init__(self, use_custom_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports
self.add_body_contacts = add_body_contacts
class _bitcell_array:
def __init__(self, use_custom_cell_arrangement):
self.use_custom_cell_arrangement = use_custom_cell_arrangement
class cell_properties():
"""
This contains meta information about the custom designed cells. For
@ -117,37 +137,65 @@ class cell_properties():
"""
def __init__(self):
self.names = {}
self.names["bitcell"] = "cell_6t"
self.names["bitcell_1rw_1r"] = "cell_1rw_1r"
self.names["bitcell_1w_1r"] = "cell_1w_1r"
self.names["dummy_bitcell"] = "dummy_cell_6t"
self.names["dummy_bitcell_1rw_1r"] = "dummy_cell_1rw_1r"
self.names["dummy_bitcell_1w_1r"] = "dummy_cell_1w_1r"
self.names["replica_bitcell"] = "replica_cell_6t"
self.names["replica_bitcell_1rw_1r"] = "replica_cell_1rw_1r"
self.names["replica_bitcell_1w_1r"] = "replica_cell_1w_1r"
self.names["col_cap_bitcell_6t"] = "col_cap_cell_6t"
self.names["col_cap_bitcell_1rw_1r"] = "col_cap_cell_1rw_1r"
self.names["col_cap_bitcell_1w_1r"] = "col_cap_cell_1w_1r"
self.names["row_cap_bitcell_6t"] = "row_cap_cell_6t"
self.names["row_cap_bitcell_1rw_1r"] = "row_cap_cell_1rw_1r"
self.names["row_cap_bitcell_1w_1r"] = "row_cap_cell_1w_1r"
self._bitcell = _bitcell._default()
self._dff = _dff(use_custom_ports = False,
custom_port_list = ["D", "Q", "clk", "vdd", "gnd"],
custom_type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
clk_pin= "clk")
self._ptx = _ptx(model_is_subckt=False,
bin_spice_models=False)
self._dff_buff = _dff_buff(use_custom_ports = False,
custom_buff_ports = ["D", "qint", "clk", "vdd", "gnd"],
add_body_contacts = False)
self._pgate = _pgate(add_implants=False)
self._dff_buff_array = _dff_buff_array(use_custom_ports = False,
add_body_contacts = False)
self._dff = _dff(use_custom_ports=False,
custom_port_list=["D", "Q", "clk", "vdd", "gnd"],
custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
clk_pin="clk")
self._dff_buff = _dff_buff(use_custom_ports=False,
custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"],
add_body_contacts=False)
self._dff_buff_array = _dff_buff_array(use_custom_ports=False,
add_body_contacts=False)
self._write_driver = _cell({'din': 'din',
'bl' : 'bl',
'br' : 'br',
'en' : 'en'})
'bl': 'bl',
'br': 'br',
'en': 'en'})
self._sense_amp = _cell({'bl' : 'bl',
'br' : 'br',
'dout' : 'dout',
'en' : 'en'})
self._sense_amp = _cell({'bl': 'bl',
'br': 'br',
'dout': 'dout',
'en': 'en'})
self._bitcell_array = _bitcell_array(use_custom_cell_arrangement = [])
self._bitcell_array = _bitcell_array(use_custom_cell_arrangement=[])
@property
def bitcell(self):
return self._bitcell
@property
def ptx(self):
return self._ptx
@property
def pgate(self):
return self._pgate
@property
def dff(self):
return self._dff

View File

@ -0,0 +1,193 @@
# 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.
#
class _bank:
def __init__(self, stack, pitch):
# bank
# column address route: stack, pitch
# m1_stack, m2_pitch (default)
# m2_stack, m3_pitch (sky130)
self.stack = stack
self.pitch = pitch
class _hierarchical_decoder:
def __init__(self,
bus_layer,
bus_directions,
input_layer,
output_layer,
vertical_supply):
# hierarchical_decoder
# bus_layer, bus_directions, bus_pitch, bus_space, input_layer, output_layer, output_layer_pitch
# m2, pref, m2_pitch, m2_space, m1, m3, m3_pitch
# m1, nonpref, m1_pitch, m2_space, m2, li, li_pitch (sky130)
#
# vertical vdd/gnd
# special jogging
self.bus_layer = bus_layer
self.bus_directions = bus_directions
self.input_layer = input_layer
self.output_layer = output_layer
self.vertical_supply = vertical_supply
class _hierarchical_predecode:
def __init__(self,
bus_layer,
bus_directions,
bus_space_factor,
input_layer,
output_layer,
vertical_supply):
# hierarchical_predecode
# bus_layer, bus_directions, bus_pitch, bus_space, input_layer, output_layer, output_layer_pitch
# m2, pref, m2_pitch, m2_space, m1, m1, m1_pitch
# m1, nonpref, m1_pitch, 1`.5*m1_space, m2, li, li_pitch (sky130)
#
# vertical vdd/gnd
# special jogging
self.bus_layer = bus_layer
self.bus_directions = bus_directions
self.bus_space_factor = bus_space_factor
self.input_layer = input_layer
self.output_layer = output_layer
self.vertical_supply = vertical_supply
class _column_mux_array:
def __init__(self,
select_layer,
select_pitch,
bitline_layer):
# column_mux_array
# sel_layer, sel_pitch, bitline_layer
# m1, m2_pitch, m2
# m3, m3_pitch, m1 (sky130)
self.select_layer = select_layer
self.select_pitch= select_pitch
self.bitline_layer = bitline_layer
class _port_address:
def __init__(self,
supply_offset):
# port_adress
# special supply offset
self.supply_offset = supply_offset
class _port_data:
def __init__(self,
channel_route_bitlines,
enable_layer):
# port_data
# connect bitlines instead of chanel route
# sense_amp_array
# en_layer
# m1
# m3 (sky130)
# precharge_array
# en_bar_layer
# m1
# m3 (sky130)
self.channel_route_bitlines = channel_route_bitlines
self.enable_layer = enable_layer
class _replica_column:
def __init__(self,
even_rows):
# replica_column
# even row check (sky130)
self.even_rows = even_rows
class _wordline_driver:
def __init__(self,
vertical_supply):
# wordline_buffer_array
# vertical vdd/gnd (sky130)
# wordline_driver_array
# vertical vdd/gnd (sky130)
# wordline_driver
# vertical vdd/gnd (sky130)
self.vertical_supply = vertical_supply
class layer_properties():
"""
This contains meta information about the module routing layers. These
can be overriden in the tech.py file.
"""
def __init__(self):
self._bank = _bank(stack="m1_stack",
pitch="m2_pitch")
self._hierarchical_decoder = _hierarchical_decoder(bus_layer="m2",
bus_directions="pref",
input_layer="m1",
output_layer="m3",
vertical_supply=False)
self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2",
bus_directions="pref",
bus_space_factor=1,
input_layer="m1",
output_layer="m1",
vertical_supply=False)
self._column_mux_array = _column_mux_array(select_layer="m1",
select_pitch="m2_pitch",
bitline_layer="m2")
self._port_address = _port_address(supply_offset=False)
self._port_data = _port_data(channel_route_bitlines=True,
enable_layer="m1")
self._replica_column = _replica_column(even_rows=False)
self._wordline_driver = _wordline_driver(vertical_supply=False)
@property
def bank(self):
return self._bank
@property
def column_mux_array(self):
return self._column_mux_array
@property
def hierarchical_decoder(self):
return self._hierarchical_decoder
@property
def hierarchical_predecode(self):
return self._hierarchical_predecode
@property
def port_address(self):
return self._port_address
@property
def port_data(self):
return self._port_data
@property
def replica_column(self):
return self._replica_column
@property
def wordline_driver(self):
return self._wordline_driver

View File

@ -8,22 +8,29 @@
from hierarchy_design import hierarchy_design
from utils import round_to_grid
import contact
from tech import preferred_directions
from tech import cell_properties as props
from globals import OPTS
import re
import debug
class design(hierarchy_design):
"""
This is the same as the hierarchy_design class except it contains
some DRC/layer constants and analytical models for other modules to reuse.
"""
def __init__(self, name):
super().__init__(name)
def __init__(self, name, cell_name=None):
# This allows us to use different GDS/spice circuits for hard cells instead of the default ones
# Except bitcell names are generated automatically by the globals.py setup_bitcells routines
# depending on the number of ports.
if name in props.names:
cell_name = props.names[name]
elif not cell_name:
cell_name = name
super().__init__(name, cell_name)
self.setup_drc_constants()
self.setup_layer_constants()
self.setup_multiport_constants()
def check_pins(self):
@ -32,7 +39,98 @@ class design(hierarchy_design):
for pin in pins:
print(pin_name, pin)
def setup_layer_constants(self):
@classmethod
def setup_drc_constants(design):
"""
These are some DRC constants used in many places
in the compiler.
"""
# Make some local rules for convenience
from tech import drc
for rule in drc.keys():
# Single layer width rules
match = re.search(r"minwidth_(.*)", rule)
if match:
if match.group(1) == "active_contact":
setattr(design, "contact_width", drc(match.group(0)))
else:
setattr(design, match.group(1) + "_width", drc(match.group(0)))
# Single layer area rules
match = re.search(r"minarea_(.*)", rule)
if match:
setattr(design, match.group(0), drc(match.group(0)))
# Single layer spacing rules
match = re.search(r"(.*)_to_(.*)", rule)
if match and match.group(1) == match.group(2):
setattr(design, match.group(1) + "_space", drc(match.group(0)))
elif match and match.group(1) != match.group(2):
if match.group(2) == "poly_active":
setattr(design, match.group(1) + "_to_contact",
drc(match.group(0)))
else:
setattr(design, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_enclose_(.*)", rule)
if match:
setattr(design, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_extend_(.*)", rule)
if match:
setattr(design, match.group(0), drc(match.group(0)))
# Create the maximum well extend active that gets used
# by cells to extend the wells for interaction with other cells
from tech import layer
design.well_extend_active = 0
if "nwell" in layer:
design.well_extend_active = max(design.well_extend_active, design.nwell_extend_active)
if "pwell" in layer:
design.well_extend_active = max(design.well_extend_active, design.pwell_extend_active)
# The active offset is due to the well extension
if "pwell" in layer:
design.pwell_enclose_active = drc("pwell_enclose_active")
else:
design.pwell_enclose_active = 0
if "nwell" in layer:
design.nwell_enclose_active = drc("nwell_enclose_active")
else:
design.nwell_enclose_active = 0
# Use the max of either so that the poly gates will align properly
design.well_enclose_active = max(design.pwell_enclose_active,
design.nwell_enclose_active,
design.active_space)
# These are for debugging previous manual rules
if False:
print("poly_width", design.poly_width)
print("poly_space", design.poly_space)
print("m1_width", design.m1_width)
print("m1_space", design.m1_space)
print("m2_width", design.m2_width)
print("m2_space", design.m2_space)
print("m3_width", design.m3_width)
print("m3_space", design.m3_space)
print("m4_width", design.m4_width)
print("m4_space", design.m4_space)
print("active_width", design.active_width)
print("active_space", design.active_space)
print("contact_width", design.contact_width)
print("poly_to_active", design.poly_to_active)
print("poly_extend_active", design.poly_extend_active)
print("poly_to_contact", design.poly_to_contact)
print("active_contact_to_gate", design.active_contact_to_gate)
print("poly_contact_to_gate", design.poly_contact_to_gate)
print("well_enclose_active", design.well_enclose_active)
print("implant_enclose_active", design.implant_enclose_active)
print("implant_space", design.implant_space)
import sys
sys.exit(1)
@classmethod
def setup_layer_constants(design):
"""
These are some layer constants used
in many places in the compiler.
@ -46,7 +144,7 @@ class design(hierarchy_design):
# Set the stack as a local helper
try:
layer_stack = getattr(tech, key)
setattr(self, key, layer_stack)
setattr(design, key, layer_stack)
except AttributeError:
pass
@ -55,14 +153,14 @@ class design(hierarchy_design):
continue
# Add the pitch
setattr(self,
setattr(design,
"{}_pitch".format(layer),
self.compute_pitch(layer, True))
design.compute_pitch(layer, True))
# Add the non-preferrd pitch (which has vias in the "wrong" way)
setattr(self,
setattr(design,
"{}_nonpref_pitch".format(layer),
self.compute_pitch(layer, False))
design.compute_pitch(layer, False))
if False:
from tech import preferred_directions
@ -73,17 +171,18 @@ class design(hierarchy_design):
continue
try:
print("{0} width {1} space {2}".format(name,
getattr(self, "{}_width".format(name)),
getattr(self, "{}_space".format(name))))
getattr(design, "{}_width".format(name)),
getattr(design, "{}_space".format(name))))
print("pitch {0} nonpref {1}".format(getattr(self, "{}_pitch".format(name)),
getattr(self, "{}_nonpref_pitch".format(name))))
print("pitch {0} nonpref {1}".format(getattr(design, "{}_pitch".format(name)),
getattr(design, "{}_nonpref_pitch".format(name))))
except AttributeError:
pass
import sys
sys.exit(1)
def compute_pitch(self, layer, preferred=True):
@staticmethod
def compute_pitch(layer, preferred=True):
"""
This is the preferred direction pitch
@ -95,13 +194,18 @@ class design(hierarchy_design):
for stack in layer_stacks:
# Compute the pitch with both vias above and below (if they exist)
if stack[0] == layer:
pitches.append(self.compute_layer_pitch(stack, preferred))
pitches.append(design.compute_layer_pitch(stack, preferred))
if stack[2] == layer:
pitches.append(self.compute_layer_pitch(stack[::-1], True))
pitches.append(design.compute_layer_pitch(stack[::-1], True))
return max(pitches)
def compute_layer_pitch(self, layer_stack, preferred):
@staticmethod
def get_preferred_direction(layer):
return preferred_directions[layer]
@staticmethod
def compute_layer_pitch(layer_stack, preferred):
(layer1, via, layer2) = layer_stack
try:
@ -113,16 +217,16 @@ class design(hierarchy_design):
contact1 = getattr(contact, layer2 + "_via")
if preferred:
if self.get_preferred_direction(layer1) == "V":
if preferred_directions[layer1] == "V":
contact_width = contact1.first_layer_width
else:
contact_width = contact1.first_layer_height
else:
if self.get_preferred_direction(layer1) == "V":
if preferred_directions[layer1] == "V":
contact_width = contact1.first_layer_height
else:
contact_width = contact1.first_layer_width
layer_space = getattr(self, layer1 + "_space")
layer_space = getattr(design, layer1 + "_space")
#print(layer_stack)
#print(contact1)
@ -130,94 +234,6 @@ class design(hierarchy_design):
return round_to_grid(pitch)
def setup_drc_constants(self):
"""
These are some DRC constants used in many places
in the compiler.
"""
# Make some local rules for convenience
from tech import drc
for rule in drc.keys():
# Single layer width rules
match = re.search(r"minwidth_(.*)", rule)
if match:
if match.group(1) == "active_contact":
setattr(self, "contact_width", drc(match.group(0)))
else:
setattr(self, match.group(1) + "_width", drc(match.group(0)))
# Single layer area rules
match = re.search(r"minarea_(.*)", rule)
if match:
setattr(self, match.group(0), drc(match.group(0)))
# Single layer spacing rules
match = re.search(r"(.*)_to_(.*)", rule)
if match and match.group(1) == match.group(2):
setattr(self, match.group(1) + "_space", drc(match.group(0)))
elif match and match.group(1) != match.group(2):
if match.group(2) == "poly_active":
setattr(self, match.group(1) + "_to_contact",
drc(match.group(0)))
else:
setattr(self, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_enclose_(.*)", rule)
if match:
setattr(self, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_extend_(.*)", rule)
if match:
setattr(self, match.group(0), drc(match.group(0)))
# Create the maximum well extend active that gets used
# by cells to extend the wells for interaction with other cells
from tech import layer
self.well_extend_active = 0
if "nwell" in layer:
self.well_extend_active = max(self.well_extend_active, self.nwell_extend_active)
if "pwell" in layer:
self.well_extend_active = max(self.well_extend_active, self.pwell_extend_active)
# The active offset is due to the well extension
if "pwell" in layer:
self.pwell_enclose_active = drc("pwell_enclose_active")
else:
self.pwell_enclose_active = 0
if "nwell" in layer:
self.nwell_enclose_active = drc("nwell_enclose_active")
else:
self.nwell_enclose_active = 0
# Use the max of either so that the poly gates will align properly
self.well_enclose_active = max(self.pwell_enclose_active,
self.nwell_enclose_active,
self.active_space)
# These are for debugging previous manual rules
if False:
print("poly_width", self.poly_width)
print("poly_space", self.poly_space)
print("m1_width", self.m1_width)
print("m1_space", self.m1_space)
print("m2_width", self.m2_width)
print("m2_space", self.m2_space)
print("m3_width", self.m3_width)
print("m3_space", self.m3_space)
print("m4_width", self.m4_width)
print("m4_space", self.m4_space)
print("active_width", self.active_width)
print("active_space", self.active_space)
print("contact_width", self.contact_width)
print("poly_to_active", self.poly_to_active)
print("poly_extend_active", self.poly_extend_active)
print("poly_to_contact", self.poly_to_contact)
print("active_contact_to_gate", self.active_contact_to_gate)
print("poly_contact_to_gate", self.poly_contact_to_gate)
print("well_enclose_active", self.well_enclose_active)
print("implant_enclose_active", self.implant_enclose_active)
print("implant_space", self.implant_space)
import sys
sys.exit(1)
def setup_multiport_constants(self):
"""
@ -266,3 +282,6 @@ class design(hierarchy_design):
total_module_power += inst.mod.analytical_power(corner, load)
return total_module_power
design.setup_drc_constants()
design.setup_layer_constants()

View File

@ -227,7 +227,7 @@ class instance(geometry):
self.mod.gds_write_file(self.gds)
# now write an instance of my module/structure
new_layout.addInstance(self.gds,
self.mod.name,
self.mod.cell_name,
offsetInMicrons=self.offset,
mirror=self.mirror,
rotate=self.rotate)
@ -402,11 +402,11 @@ class instance(geometry):
def __str__(self):
""" override print function output """
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.cell_name + " " + self.mirror + " R=" + str(self.rotate) + ")"
def __repr__(self):
""" override print function output """
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.cell_name + " " + self.mirror + " R=" + str(self.rotate) + ")"
class path(geometry):

View File

@ -20,9 +20,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
"""
name_map = []
def __init__(self, name):
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds"
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
def __init__(self, name, cell_name):
self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds"
self.sp_file = OPTS.openram_tech + "sp_lib/" + cell_name + ".sp"
# If we have a separate lvs directory, then all the lvs files
# should be in there (all or nothing!)
@ -33,7 +33,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
if os.path.exists(lvs_dir):
self.lvs_file = lvs_dir + name + ".sp"
self.lvs_file = lvs_dir + cell_name + ".sp"
else:
self.lvs_file = self.sp_file
@ -41,8 +41,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.lvs_errors = "skipped"
self.name = name
hierarchy_spice.spice.__init__(self, name)
hierarchy_layout.layout.__init__(self, name)
self.cell_name = cell_name
hierarchy_spice.spice.__init__(self, name, cell_name)
hierarchy_layout.layout.__init__(self, name, cell_name)
self.init_graph_params()
def get_layout_pins(self, inst):
@ -76,20 +77,20 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.lvs_write(tempspice)
self.gds_write(tempgds)
# Final verification option does not allow nets to be connected by label.
self.drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification)
self.lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
self.drc_errors = verify.run_drc(self.cell_name, tempgds, extract=True, final_verification=final_verification)
self.lvs_errors = verify.run_lvs(self.cell_name, tempgds, tempspice, final_verification=final_verification)
# force_check is used to determine decoder height and other things, so we shouldn't fail
# if that flag is set
if OPTS.inline_lvsdrc and not force_check:
debug.check(self.drc_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name,
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
self.drc_errors))
debug.check(self.lvs_errors == 0,
"LVS failed for {0} with {1} errors(s)".format(self.name,
"LVS failed for {0} with {1} errors(s)".format(self.cell_name,
self.lvs_errors))
if OPTS.purge_temp:
if not OPTS.keep_temp:
os.remove(tempspice)
os.remove(tempgds)
@ -104,14 +105,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only:
return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.cell_name)
self.gds_write(tempgds)
num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification)
num_errors = verify.run_drc(self.cell_name, tempgds, final_verification=final_verification)
debug.check(num_errors == 0,
"DRC failed for {0} with {1} error(s)".format(self.name,
"DRC failed for {0} with {1} error(s)".format(self.cell_name,
num_errors))
if OPTS.purge_temp:
if not OPTS.keep_temp:
os.remove(tempgds)
def LVS(self, final_verification=False):
@ -125,15 +126,15 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
if OPTS.netlist_only:
return
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name)
tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.cell_name)
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
self.lvs_write(tempspice)
self.gds_write(tempgds)
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_errors == 0,
"LVS failed for {0} with {1} error(s)".format(self.name,
"LVS failed for {0} with {1} error(s)".format(self.cell_name,
num_errors))
if OPTS.purge_temp:
if not OPTS.keep_temp:
os.remove(tempspice)
os.remove(tempgds)
@ -217,7 +218,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
pins = ",".join(self.pins)
insts = [" {}".format(x) for x in self.insts]
objs = [" {}".format(x) for x in self.objs]
s = "********** design {0} **********".format(self.name)
s = "********** design {0} **********".format(self.cell_name)
s += "\n pins ({0})={1}\n".format(len(self.pins), pins)
s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs))
s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))

View File

@ -31,8 +31,9 @@ class layout():
layout/netlist and perform LVS/DRC.
"""
def __init__(self, name):
def __init__(self, name, cell_name):
self.name = name
self.cell_name = cell_name
self.width = None
self.height = None
self.bounding_box = None
@ -215,7 +216,7 @@ class layout():
# 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.name, name))
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name))
self.inst_names.add(name)
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
@ -316,7 +317,7 @@ class layout():
return any_pin
except Exception:
self.gds_write("missing_pin.gds")
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.name), -1)
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.cell_name), -1)
def get_pins(self, text):
"""
@ -537,10 +538,6 @@ class layout():
position_list=coordinates,
widen_short_wires=widen_short_wires)
def get_preferred_direction(self, layer):
""" Return the preferred routing directions """
return preferred_directions[layer]
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
""" Add a three layer via structure. """
from sram_factory import factory

View File

@ -10,6 +10,7 @@ import re
import os
import math
import tech
from pprint import pformat
from delay_data import delay_data
from wire_spice_model import wire_spice_model
from power_data import power_data
@ -26,8 +27,9 @@ class spice():
Class consisting of a set of modules and instances of these modules
"""
def __init__(self, name):
def __init__(self, name, cell_name):
self.name = name
self.cell_name = cell_name
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
# Holds subckts/mods for this module
@ -164,7 +166,6 @@ class spice():
num_pins = len(self.insts[-1].mod.pins)
num_args = len(args)
if (check and num_pins != num_args):
from pprint import pformat
if num_pins < num_args:
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins)
arg_pins = args
@ -181,7 +182,6 @@ class spice():
self.conns.append(args)
if check and (len(self.insts)!=len(self.conns)):
from pprint import pformat
insts_string=pformat(self.insts)
conns_string=pformat(self.conns)
@ -214,7 +214,7 @@ class spice():
f.close()
# find the correct subckt line in the file
subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE)
subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE)
subckt_line = list(filter(subckt.search, self.spice))[0]
# parses line into ports and remove subckt
self.pins = subckt_line.split(" ")[2:]
@ -234,7 +234,7 @@ class spice():
# pins and subckt should be the same
# find the correct subckt line in the file
subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE)
subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE)
subckt_line = list(filter(subckt.search, self.lvs))[0]
# parses line into ports and remove subckt
lvs_pins = subckt_line.split(" ")[2:]
@ -293,7 +293,7 @@ class spice():
return
# write out the first spice line (the subcircuit)
sp.write("\n.SUBCKT {0} {1}\n".format(self.name,
sp.write("\n.SUBCKT {0} {1}\n".format(self.cell_name,
" ".join(self.pins)))
for pin in self.pins:
@ -304,7 +304,7 @@ class spice():
# every instance must have a set of connections, even if it is empty.
if len(self.insts) != len(self.conns):
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name,
len(self.insts),
len(self.conns)))
debug.error("Instances: \n" + str(self.insts))
@ -330,9 +330,9 @@ class spice():
else:
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
" ".join(self.conns[i]),
self.insts[i].mod.name))
self.insts[i].mod.cell_name))
sp.write(".ENDS {0}\n".format(self.name))
sp.write(".ENDS {0}\n".format(self.cell_name))
else:
# If spice is a hard module, output the spice file contents.
@ -390,7 +390,7 @@ class spice():
.format(self.__class__.__name__))
debug.warning("Class {0} name {1}"
.format(self.__class__.__name__,
self.name))
self.cell_name))
return None
def get_cin(self):
@ -408,7 +408,7 @@ class spice():
.format(self.__class__.__name__))
debug.warning("Class {0} name {1}"
.format(self.__class__.__name__,
self.name))
self.cell_name))
return 0
def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5):

View File

@ -5,14 +5,9 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import gdsMill
import tech
import globals
import math
import debug
import datetime
from collections import defaultdict
import pdb
from tech import layer_names
class lef:
"""
@ -20,13 +15,13 @@ class lef:
and write them to LEF file.
This is inherited by the sram_base class.
"""
def __init__(self,layers):
def __init__(self, layers):
# LEF db units per micron
self.lef_units = 2000
# These are the layers of the obstructions
self.lef_layers = layers
# Round to ensure float values are divisible by 0.0025 (the manufacturing grid)
self.round_grid = 4;
self.round_grid = 4
def lef_write(self, lef_name):
"""Write the entire lef of the object to the file."""
@ -34,7 +29,7 @@ class lef:
self.indent = "" # To maintain the indent level easily
self.lef = open(lef_name,"w")
self.lef = open(lef_name, "w")
self.lef_write_header()
for pin in self.pins:
self.lef_write_pin(pin)
@ -52,30 +47,29 @@ class lef:
self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
self.lef.write("END UNITS\n")
self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name))
self.lef.write("{0}MACRO {1}\n".format(self.indent, self.name))
self.indent += " "
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent,
round(self.width,self.round_grid),
round(self.height,self.round_grid)))
round(self.width, self.round_grid),
round(self.height, self.round_grid)))
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
def lef_write_footer(self):
self.lef.write("{0}END {1}\n".format(self.indent,self.name))
self.lef.write("{0}END {1}\n".format(self.indent, self.name))
self.indent = self.indent[:-3]
self.lef.write("END LIBRARY\n")
def lef_write_pin(self, name):
pin_dir = self.get_pin_dir(name)
pin_type = self.get_pin_type(name)
self.lef.write("{0}PIN {1}\n".format(self.indent,name))
self.lef.write("{0}PIN {1}\n".format(self.indent, name))
self.indent += " "
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir))
self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent, pin_dir))
if pin_type in ["POWER","GROUND"]:
self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type))
if pin_type in ["POWER", "GROUND"]:
self.lef.write("{0}USE {1} ; \n".format(self.indent, pin_type))
self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent))
self.lef.write("{0}PORT\n".format(self.indent))
@ -84,7 +78,7 @@ class lef:
# We could sort these together to minimize different layer sections, but meh.
pin_list = self.get_pins(name)
for pin in pin_list:
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer))
self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[pin.layer]))
self.lef_write_shape(pin.rect)
# End the PORT
@ -93,19 +87,16 @@ class lef:
# End the PIN
self.indent = self.indent[:-3]
self.lef.write("{0}END {1}\n".format(self.indent,name))
self.lef.write("{0}END {1}\n".format(self.indent, name))
def lef_write_obstructions(self):
""" Write all the obstructions on each layer """
self.lef.write("{0}OBS\n".format(self.indent))
for layer in self.lef_layers:
self.lef.write("{0}LAYER {1} ;\n".format(self.indent,layer))
self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[layer]))
self.indent += " "
# pdb.set_trace()
blockages = self.get_blockages(layer,True)
blockages = self.get_blockages(layer, True)
for b in blockages:
# if len(b) > 2:
# print(b)
self.lef_write_shape(b)
self.indent = self.indent[:-3]
self.lef.write("{0}END\n".format(self.indent))
@ -116,13 +107,19 @@ class lef:
self.lef.write("{0}RECT ".format(self.indent))
for item in rect:
# print(rect)
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
self.lef.write(" {0} {1}".format(round(item[0],
self.round_grid),
round(item[1],
self.round_grid)))
self.lef.write(" ;\n")
else:
""" Write a LEF polygon """
self.lef.write("{0}POLYGON ".format(self.indent))
for item in rect:
self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid)))
self.lef.write(" {0} {1}".format(round(item[0],
self.round_grid),
round(item[1],
self.round_grid)))
# for i in range(0,len(rect)):
# self.lef.write(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid)))
self.lef.write(" ;\n")

View File

@ -4,10 +4,12 @@
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import os
import math
import gdsMill
import tech
import math
import globals
import debug
from vector import vector
@ -57,10 +59,11 @@ def auto_measure_libcell(pin_list, name, units, lpp):
Return these as a set of properties including the cell width/height too.
"""
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(cell_gds)
cell_vlsi = _get_gds_reader(units, cell_gds)
# FIXME: This duplicates a lot of functionality of get_gds_size and
# get_gds_pins, it should probably just call those functions?
cell = {}
measure_result = cell_vlsi.getLayoutBorder(lpp[0])
if measure_result:
@ -73,20 +76,45 @@ def auto_measure_libcell(pin_list, name, units, lpp):
return cell
_GDS_READER_CACHE = {}
def _get_gds_reader(units, gds_filename):
gds_absname = os.path.realpath(gds_filename)
k = (units, gds_absname)
try:
return _GDS_READER_CACHE[k]
except KeyError:
debug.info(4, "Creating VLSI layout from {}".format(gds_absname))
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_absname)
_GDS_READER_CACHE[k] = cell_vlsi
return cell_vlsi
_GDS_SIZE_CACHE = {}
def get_gds_size(name, gds_filename, units, lpp):
"""
Open a GDS file and return the size from either the
bounding box or a border layer.
"""
debug.info(4, "Creating VLSI layout for {}".format(name))
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_filename)
k = (name, os.path.realpath(gds_filename), units, lpp)
try:
return _GDS_SIZE_CACHE[k]
except KeyError:
cell_vlsi = _get_gds_reader(units, gds_filename)
measure_result = cell_vlsi.getLayoutBorder(lpp)
if not measure_result:
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
measure_result = cell_vlsi.measureSize(name)
_GDS_SIZE_CACHE[k] = measure_result
# returns width,height
return measure_result
@ -101,14 +129,19 @@ def get_libcell_size(name, units, lpp):
return(get_gds_size(name, cell_gds, units, lpp))
_GDS_PINS_CACHE = {}
def get_gds_pins(pin_names, name, gds_filename, units):
"""
Open a GDS file and find the pins in pin_names as text on a given layer.
Return these as a rectangle layer pair for each pin.
"""
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_filename)
k = (tuple(pin_names), name, os.path.realpath(gds_filename), units)
try:
return dict(_GDS_PINS_CACHE[k])
except KeyError:
cell_vlsi = _get_gds_reader(units, gds_filename)
cell = {}
for pin_name in pin_names:
@ -121,7 +154,9 @@ def get_gds_pins(pin_names, name, gds_filename, units):
# this is a list because other cells/designs
# may have must-connect pins
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
return cell
_GDS_PINS_CACHE[k] = cell
return dict(cell)
def get_libcell_pins(pin_list, name, units):
@ -132,7 +167,3 @@ def get_libcell_pins(pin_list, name, units):
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
return(get_gds_pins(pin_list, name, cell_gds, units))

View File

@ -6,11 +6,9 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
from globals import OPTS
class bitcell(bitcell_base.bitcell_base):
"""
@ -20,8 +18,6 @@ class bitcell(bitcell_base.bitcell_base):
library.
"""
# If we have a split WL bitcell, if not be backwards
# compatible in the tech file
pin_names = [props.bitcell.cell_6t.pin.bl,
props.bitcell.cell_6t.pin.br,
props.bitcell.cell_6t.pin.wl,
@ -30,24 +26,12 @@ class bitcell(bitcell_base.bitcell_base):
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "cell_6t")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create bitcell")
self.width = bitcell.width
self.height = bitcell.height
self.pin_map = bitcell.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
#debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells")
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = [props.bitcell.cell_6t.pin.wl]

View File

@ -6,10 +6,7 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer, parameter, drc
from tech import cell_properties as props
import logical_effort
import bitcell_base
@ -29,27 +26,17 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create bitcell with 1RW and 1R Port")
self.width = bitcell_1rw_1r.width
self.height = bitcell_1rw_1r.height
self.pin_map = bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
pin_names = bitcell_1rw_1r.pin_names
pin_names = self.pin_names
self.bl_names = [pin_names[0], pin_names[2]]
self.br_names = [pin_names[1], pin_names[3]]
self.wl_names = [pin_names[4], pin_names[5]]

View File

@ -6,8 +6,6 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -31,28 +29,18 @@ class bitcell_1w_1r(bitcell_base.bitcell_base):
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "cell_1w_1r")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create bitcell with 1W and 1R Port")
self.width = bitcell_1w_1r.width
self.height = bitcell_1w_1r.height
self.pin_map = bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
pin_names = bitcell_1w_1r.pin_names
pin_names = self.pin_names
self.bl_names = [pin_names[0], pin_names[2]]
self.br_names = [pin_names[1], pin_names[3]]
self.wl_names = [pin_names[4], pin_names[5]]
def get_bitcell_pins(self, col, row):
"""
Creates a list of connections in the bitcell,

View File

@ -8,18 +8,30 @@
import debug
import design
import utils
from globals import OPTS
import logical_effort
from tech import parameter, drc, layer
from tech import GDS, parameter, drc, layer
class bitcell_base(design.design):
"""
Base bitcell parameters to be over-riden.
"""
def __init__(self, name):
cell_size_layer = "boundary"
def __init__(self, name, hard_cell=True):
design.design.__init__(self, name)
if hard_cell:
(self.width, self.height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
self.pin_map = utils.get_libcell_pins(self.pin_names,
self.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

View File

@ -6,39 +6,25 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
"""
todo"""
Column end cap cell.
"""
pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
props.bitcell.cell_1rw1r.pin.br0,
props.bitcell.cell_1rw1r.pin.bl1,
props.bitcell.cell_1rw1r.pin.br1,
props.bitcell.cell_1rw1r.pin.vdd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"POWER", "GROUND"]
(width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"col_cap_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r")
def __init__(self, name="col_cap_cell_1rw_1r"):
bitcell_base.bitcell_base.__init__(self, name)
debug.info(2, "Create col_cap bitcell 1rw+1r object")
self.width = col_cap_bitcell_1rw_1r.width
self.height = col_cap_bitcell_1rw_1r.height
self.pin_map = col_cap_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.no_instances = True

View File

@ -6,8 +6,6 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -24,19 +22,10 @@ class dummy_bitcell(bitcell_base.bitcell_base):
props.bitcell.cell_6t.pin.wl,
props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_6t")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create dummy bitcell")
self.width = dummy_bitcell.width
self.height = dummy_bitcell.height
self.pin_map = dummy_bitcell.pin_map

View File

@ -6,8 +6,6 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -27,23 +25,11 @@ class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create dummy bitcell 1rw+1r object")
self.width = dummy_bitcell_1rw_1r.width
self.height = dummy_bitcell_1rw_1r.height
self.pin_map = dummy_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)

View File

@ -6,8 +6,6 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -29,21 +27,11 @@ class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1w_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create dummy bitcell 1w+1r object")
self.width = dummy_bitcell_1w_1r.width
self.height = dummy_bitcell_1w_1r.height
self.pin_map = dummy_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)

View File

@ -7,17 +7,17 @@
#
import debug
import design
from tech import drc, spice,parameter
from vector import vector
from globals import OPTS
from sram_factory import factory
class dummy_pbitcell(design.design):
"""
Creates a replica bitcell using pbitcell
"""
def __init__(self, name):
def __init__(self, name, cell_name=None):
self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports
@ -54,7 +54,8 @@ class dummy_pbitcell(design.design):
self.add_pin("gnd")
def add_modules(self):
self.prbc = factory.create(module_type="pbitcell",dummy_bitcell=True)
self.prbc = factory.create(module_type="pbitcell",
dummy_bitcell=True)
self.add_mod(self.prbc)
self.height = self.prbc.height
@ -75,7 +76,7 @@ class dummy_pbitcell(design.design):
self.connect_inst(temp)
def place_pbitcell(self):
self.prbc_inst.place(offset=vector(0,0))
self.prbc_inst.place(offset=vector(0, 0))
def route_rbc_connections(self):
for port in range(self.total_ports):

View File

@ -30,7 +30,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.replica_bitcell = replica_bitcell
self.dummy_bitcell = dummy_bitcell
bitcell_base.bitcell_base.__init__(self, name)
bitcell_base.bitcell_base.__init__(self, name, hard_cell=False)
fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
info_string = fmt_str.format(self.num_rw_ports,
self.num_w_ports,

View File

@ -5,15 +5,14 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter,cell_properties
import bitcell_base
from tech import cell_properties as props
from tech import parameter, drc
import logical_effort
from globals import OPTS
class replica_bitcell(design.design):
class replica_bitcell(bitcell_base.bitcell_base):
"""
A single bit cell (6T, 8T, etc.)
This module implements the single memory cell used in the design. It
@ -27,42 +26,29 @@ class replica_bitcell(design.design):
props.bitcell.cell_6t.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
else:
(width,height) = (0,0)
pin_map = []
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_6t")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create replica bitcell object")
self.width = replica_bitcell.width
self.height = replica_bitcell.height
self.pin_map = replica_bitcell.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 # min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
dynamic = 0 # FIXME
total_power = self.return_power(dynamic, leakage)
return total_power

View File

@ -5,13 +5,14 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter
import bitcell_base
from tech import cell_properties as props
from tech import parameter, drc
import logical_effort
class replica_bitcell_1rw_1r(design.design):
class replica_bitcell_1rw_1r(bitcell_base.bitcell_base):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
@ -26,42 +27,33 @@ class replica_bitcell_1rw_1r(design.design):
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1rw_1r")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create replica bitcell 1rw+1r object")
self.width = replica_bitcell_1rw_1r.width
self.height = replica_bitcell_1rw_1r.height
self.pin_map = replica_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 # min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
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
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
pins = props.bitcell.cell_1rw1r.pin
#Edges hardcoded here. Essentially wl->bl/br for both ports.
# Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self)
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self)

View File

@ -5,13 +5,14 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter
import bitcell_base
from tech import cell_properties as props
from tech import parameter, drc
import logical_effort
class replica_bitcell_1w_1r(design.design):
class replica_bitcell_1w_1r(bitcell_base.bitcell_base):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
@ -26,43 +27,34 @@ class replica_bitcell_1w_1r(design.design):
props.bitcell.cell_1w1r.pin.wl1,
props.bitcell.cell_1w1r.pin.vdd,
props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "replica_cell_1w_1r")
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create replica bitcell 1w+1r object")
self.width = replica_bitcell_1w_1r.width
self.height = replica_bitcell_1w_1r.height
self.pin_map = replica_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
size = 0.5 # This accounts for bitline being drained thought the access TX and internal node
cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 # min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
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
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
debug.info(1,'Adding edges for {}'.format(inst_name))
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
debug.info(1, 'Adding edges for {}'.format(inst_name))
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
pins = props.bitcell.cell_1w1r.pin
#Edges hardcoded here. Essentially wl->bl/br for the read port.
# Edges hardcoded here. Essentially wl->bl/br for the read port.
# Port 1 edges
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)

View File

@ -7,23 +7,25 @@
#
import debug
import design
from tech import drc, spice,parameter
from vector import vector
from globals import OPTS
from sram_factory import factory
class replica_pbitcell(design.design):
"""
Creates a replica bitcell using pbitcell
"""
def __init__(self, name):
def __init__(self, name, cell_name=None):
if not cell_name:
cell_name = name
self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
design.design.__init__(self, name)
design.design.__init__(self, name, cell_name)
debug.info(1, "create a replica bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports,
self.num_r_ports))
@ -54,7 +56,8 @@ class replica_pbitcell(design.design):
self.add_pin("gnd")
def add_modules(self):
self.prbc = factory.create(module_type="pbitcell",replica_bitcell=True)
self.prbc = factory.create(module_type="pbitcell",
replica_bitcell=True)
self.add_mod(self.prbc)
self.height = self.prbc.height
@ -75,7 +78,7 @@ class replica_pbitcell(design.design):
self.connect_inst(temp)
def place_pbitcell(self):
self.prbc_inst.place(offset=vector(0,0))
self.prbc_inst.place(offset=vector(0, 0))
def route_rbc_connections(self):
for port in range(self.total_ports):

View File

@ -6,39 +6,22 @@
# All rights reserved.
#
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
Row end cap cell.
"""
pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["INPUT", "INPUT", "GROUND"]
(width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"row_cap_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r")
def __init__(self, name="row_cap_cell_1rw_1r"):
bitcell_base.bitcell_base.__init__(self, name)
debug.info(2, "Create row_cap bitcell 1rw+1r object")
self.width = row_cap_bitcell_1rw_1r.width
self.height = row_cap_bitcell_1rw_1r.height
self.pin_map = row_cap_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.no_instances = True

View File

@ -306,7 +306,8 @@ class delay(simulation):
self.create_test_cycles()
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.delay_stim_sp = "delay_stim.sp"
temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.delay_stim_sp)
self.sf = open(temp_stim, "w")
self.sf.write("* Delay stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(self.period,
self.load,
@ -350,7 +351,8 @@ class delay(simulation):
self.check_arguments()
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.power_stim_sp = "power_stim.sp"
temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.power_stim_sp)
self.sf = open(temp_stim, "w")
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
self.stim = stimuli(self.sf, self.corner)
@ -616,7 +618,7 @@ class delay(simulation):
self.write_delay_stimulus()
self.stim.run_sim()
self.stim.run_sim(self.delay_stim_sp)
return self.check_measurements()
@ -772,7 +774,7 @@ class delay(simulation):
debug.info(1, "Performing leakage power simulations.")
self.write_power_stimulus(trim=False)
self.stim.run_sim()
self.stim.run_sim(self.power_stim_sp)
leakage_power=parse_spice_list("timing", "leakage_power")
debug.check(leakage_power!="Failed", "Could not measure leakage power.")
debug.info(1, "Leakage power of full array is {0} mW".format(leakage_power * 1e3))
@ -780,7 +782,7 @@ class delay(simulation):
# sys.exit(1)
self.write_power_stimulus(trim=True)
self.stim.run_sim()
self.stim.run_sim(self.power_stim_sp)
trim_leakage_power=parse_spice_list("timing", "leakage_power")
debug.check(trim_leakage_power!="Failed", "Could not measure leakage power.")
debug.info(1, "Leakage power of trimmed array is {0} mW".format(trim_leakage_power * 1e3))

View File

@ -21,13 +21,24 @@ class functional(simulation):
for successful SRAM operation.
"""
def __init__(self, sram, spfile, corner, cycles=15):
def __init__(self, sram, spfile, corner=None, cycles=15, period=None, output_path=None):
super().__init__(sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests
if OPTS.is_unit_test:
random.seed(12345)
if not corner:
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
if period:
self.period = period
if not output_path:
self.output_path = OPTS.openram_temp
else:
self.output_path = output_path
if self.write_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
@ -58,15 +69,14 @@ class functional(simulation):
self.read_check = []
self.read_results = []
def run(self, feasible_period=None):
if feasible_period: # period defaults to tech.py feasible period otherwise.
self.period = feasible_period
# Generate a random sequence of reads and writes
self.create_random_memory_sequence()
# Run SPICE simulation
# Write SPICE simulation
self.write_functional_stimulus()
self.stim.run_sim()
def run(self):
self.stim.run_sim(self.stim_sp)
# read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error.
(success, error) = self.read_stim_results()
@ -330,7 +340,8 @@ class functional(simulation):
def write_functional_stimulus(self):
""" Writes SPICE stimulus. """
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.stim_sp = "functional_stim.sp"
temp_stim = "{0}/{1}".format(self.output_path, self.stim_sp)
self.sf = open(temp_stim, "w")
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
self.stim = stimuli(self.sf, self.corner)

View File

@ -27,23 +27,22 @@ class setup_hold():
self.model_location = OPTS.openram_tech + "sp_lib/dff.sp"
self.period = tech.spice["feasible_period"]
debug.info(2,"Feasible period from technology file: {0} ".format(self.period))
debug.info(2, "Feasible period from technology file: {0} ".format(self.period))
self.set_corner(corner)
def set_corner(self,corner):
def set_corner(self, corner):
""" Set the corner values """
self.corner = corner
(self.process, self.vdd_voltage, self.temperature) = corner
self.gnd_voltage = 0
def write_stimulus(self, mode, target_time, correct_value):
"""Creates a stimulus file for SRAM setup/hold time calculation"""
# creates and opens the stimulus file for writing
temp_stim = OPTS.openram_temp + "stim.sp"
self.stim_sp = "sh_stim.sp"
temp_stim = OPTS.openram_temp + self.stim_sp
self.sf = open(temp_stim, "w")
self.stim = stimuli(self.sf, self.corner)
@ -63,8 +62,7 @@ class setup_hold():
self.write_measures(mode=mode,
correct_value=correct_value)
self.stim.write_control(4*self.period)
self.stim.write_control(4 * self.period)
self.sf.close()
@ -79,7 +77,6 @@ class setup_hold():
self.sf.write("\n* Global Power Supplies\n")
self.stim.write_supply()
def write_data(self, mode, target_time, correct_value):
"""Create the data signals for setup/hold analysis. First period is to
initialize it to the opposite polarity. Second period is used for
@ -113,14 +110,12 @@ class setup_hold():
# without using .IC on an internal node.
# Return input to value after one period.
# The second pulse is the characterization one at 2*period
clk_times=[0, 0.1*self.period,self.period,2*self.period],
clk_times=[0, 0.1 * self.period, self.period, 2 * self.period],
data_values=[0, 1, 0, 1],
period=2*self.period,
period=2 * self.period,
slew=self.constrained_input_slew,
setup=0)
def write_measures(self, mode, correct_value):
""" Measure statements for setup/hold with right phases. """
@ -139,7 +134,6 @@ class setup_hold():
else:
din_rise_or_fall = "RISE"
self.sf.write("\n* Measure statements for pass/fail verification\n")
trig_name = "clk"
targ_name = "dout"
@ -152,8 +146,8 @@ class setup_hold():
targ_val=targ_val,
trig_dir="RISE",
targ_dir=dout_rise_or_fall,
trig_td=1.9*self.period,
targ_td=1.9*self.period)
trig_td=1.9 * self.period,
targ_td=1.9 * self.period)
targ_name = "data"
# Start triggers right after initialize value is returned to normal
@ -165,11 +159,8 @@ class setup_hold():
targ_val=targ_val,
trig_dir="RISE",
targ_dir=din_rise_or_fall,
trig_td=1.2*self.period,
targ_td=1.2*self.period)
trig_td=1.2 * self.period,
targ_td=1.2 * self.period)
def bidir_search(self, correct_value, mode):
""" This will perform a bidirectional search for either setup or hold times.
@ -182,23 +173,26 @@ class setup_hold():
# this time. They are also unbalanced so that the average won't be right on the clock edge in the
# first iteration.
if mode == "SETUP":
feasible_bound = 1.25*self.period
infeasible_bound = 2.5*self.period
feasible_bound = 1.25 * self.period
infeasible_bound = 2.5 * self.period
else:
infeasible_bound = 1.5*self.period
feasible_bound = 2.75*self.period
infeasible_bound = 1.5 * self.period
feasible_bound = 2.75 * self.period
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
self.write_stimulus(mode=mode,
target_time=feasible_bound,
correct_value=correct_value)
self.stim.run_sim()
self.stim.run_sim(self.stim_sp)
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time))
if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float:
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,ideal_clk_to_q,setuphold_time),2)
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,
ideal_clk_to_q,
setuphold_time),
2)
if mode == "SETUP": # SETUP is clk-din, not din-clk
setuphold_time *= -1e9
@ -206,57 +200,53 @@ class setup_hold():
setuphold_time *= 1e9
passing_setuphold_time = setuphold_time
debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
setuphold_time,
feasible_bound,
2*self.period))
2 * self.period))
#raw_input("Press Enter to continue...")
while True:
target_time = (feasible_bound + infeasible_bound)/2
target_time = (feasible_bound + infeasible_bound) / 2
self.write_stimulus(mode=mode,
target_time=target_time,
correct_value=correct_value)
debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
correct_value,
target_time,
infeasible_bound,
feasible_bound))
self.stim.run_sim()
self.stim.run_sim(self.stim_sp)
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float:
if type(clk_to_q) == float and (clk_to_q < 1.1 * ideal_clk_to_q) and type(setuphold_time)==float:
if mode == "SETUP": # SETUP is clk-din, not din-clk
setuphold_time *= -1e9
else:
setuphold_time *= 1e9
debug.info(2,"PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time))
debug.info(2, "PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
passing_setuphold_time = setuphold_time
feasible_bound = target_time
else:
debug.info(2,"FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time))
debug.info(2, "FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
infeasible_bound = target_time
#raw_input("Press Enter to continue...")
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound))
debug.info(3, "CONVERGE {0} vs {1}".format(feasible_bound, infeasible_bound))
break
debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time))
debug.info(2, "Converged on {0} time {1}.".format(mode, passing_setuphold_time))
return passing_setuphold_time
def setup_LH_time(self):
"""Calculates the setup time for low-to-high transition for a DFF
"""
return self.bidir_search(1, "SETUP")
def setup_HL_time(self):
"""Calculates the setup time for high-to-low transition for a DFF
"""
@ -272,7 +262,6 @@ class setup_hold():
"""
return self.bidir_search(0, "HOLD")
def analyze(self, related_slews, constrained_slews):
"""main function to calculate both setup and hold time for the
DFF and returns a dictionary that contains 4 lists for both
@ -301,10 +290,10 @@ class setup_hold():
# }
# return times
for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews:
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew))
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,
self.constrained_input_slew))
LH_setup_time = self.setup_LH_time()
debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time))
HL_setup_time = self.setup_HL_time()
@ -325,7 +314,7 @@ class setup_hold():
}
return times
def analytical_setuphold(self,related_slews, constrained_slews):
def analytical_setuphold(self, related_slews, constrained_slews):
""" Just return the fixed setup/hold times from the technology.
"""
LH_setup = []
@ -336,10 +325,10 @@ class setup_hold():
for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews:
# convert from ps to ns
LH_setup.append(tech.spice["dff_setup"]/1e3)
HL_setup.append(tech.spice["dff_setup"]/1e3)
LH_hold.append(tech.spice["dff_hold"]/1e3)
HL_hold.append(tech.spice["dff_hold"]/1e3)
LH_setup.append(tech.spice["dff_setup"] / 1e3)
HL_setup.append(tech.spice["dff_setup"] / 1e3)
LH_hold.append(tech.spice["dff_hold"] / 1e3)
HL_hold.append(tech.spice["dff_hold"] / 1e3)
times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup,

View File

@ -33,10 +33,21 @@ class stimuli():
self.sf = stim_file
(self.process, self.voltage, self.temperature) = corner
found = False
self.device_libraries = []
self.device_models = []
try:
self.device_libraries = tech.spice["fet_libraries"][self.process]
except:
self.device_models = tech.spice["fet_models"][self.process]
self.device_libraries += tech.spice["fet_libraries"][self.process]
found = True
except KeyError:
pass
try:
self.device_models += tech.spice["fet_models"][self.process]
found = True
except KeyError:
pass
if not found:
debug.error("Must define either fet_libraries or fet_models.", -1)
def inst_model(self, pins, model_name):
""" Function to instantiate a generic model with a set of pins """
@ -249,7 +260,7 @@ class stimuli():
# create plots for all signals
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
if OPTS.debug_level>0:
if OPTS.verbose_level>0:
if OPTS.spice_name in ["hspice", "xa"]:
self.sf.write(".probe V(*)\n")
else:
@ -265,21 +276,16 @@ class stimuli():
"""Writes include statements, inputs are lists of model files"""
self.sf.write("* {} process corner\n".format(self.process))
if OPTS.tech_name == "sky130":
for item in self.device_libraries:
if os.path.isfile(item[0]):
self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1]))
else:
debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
includes = [circuit]
else:
includes = self.device_models + [circuit]
for item in list(includes):
if os.path.isfile(item):
self.sf.write(".include \"{0}\"\n".format(item))
else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
def write_supply(self):
""" Writes supply voltage statements """
@ -290,9 +296,9 @@ class stimuli():
self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n")
self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
def run_sim(self):
def run_sim(self, name):
""" Run hspice in batch mode and output rawfile to parse. """
temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
temp_stim = "{0}{1}".format(OPTS.openram_temp, name)
import datetime
start_time = datetime.datetime.now()
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")

View File

@ -6,7 +6,7 @@
# All rights reserved.
#
import design
from tech import GDS, layer, spice, parameter
from tech import GDS, layer, spice
from tech import cell_properties as props
import utils
@ -23,18 +23,22 @@ class dff(design.design):
pin_names = props.dff.custom_port_list
type_list = props.dff.custom_type_list
clk_pin = props.dff.clk_pin
(width, height) = utils.get_libcell_size("dff",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
cell_size_layer = "boundary"
def __init__(self, name="dff"):
design.design.__init__(self, name)
super().__init__(name)
self.width = dff.width
self.height = dff.height
self.pin_map = dff.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def analytical_power(self, corner, load):
@ -49,7 +53,6 @@ class dff(design.design):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
from tech import parameter
c_load = load
c_para = spice["dff_out_cap"]#ff
transition_prob = 0.5

View File

@ -9,7 +9,6 @@ import design
from tech import GDS, layer, spice, parameter
import logical_effort
import utils
import debug
class inv_dec(design.design):
@ -19,18 +18,22 @@ class inv_dec(design.design):
pin_names = ["A", "Z", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("inv_dec",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"])
cell_size_layer = "boundary"
def __init__(self, name="inv_dec", height=None):
design.design.__init__(self, name)
super().__init__(name)
self.width = inv_dec.width
self.height = inv_dec.height
self.pin_map = inv_dec.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def analytical_power(self, corner, load):

View File

@ -18,18 +18,22 @@ class nand2_dec(design.design):
pin_names = ["A", "B", "Z", "vdd", "gnd"]
type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("nand2_dec",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"])
cell_size_layer = "boundary"
def __init__(self, name="nand2_dec", height=None):
design.design.__init__(self, name)
super().__init__(name)
self.width = nand2_dec.width
self.height = nand2_dec.height
self.pin_map = nand2_dec.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
# FIXME: For now...

View File

@ -18,18 +18,22 @@ class nand3_dec(design.design):
pin_names = ["A", "B", "C", "Z", "vdd", "gnd"]
type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("nand3_dec",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"])
cell_size_layer = "boundary"
def __init__(self, name="nand3_dec", height=None):
design.design.__init__(self, name)
super().__init__(name)
self.width = nand3_dec.width
self.height = nand3_dec.height
self.pin_map = nand3_dec.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
# FIXME: For now...

View File

@ -18,18 +18,22 @@ class nand4_dec(design.design):
pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("nand4_dec",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"])
cell_size_layer = "boundary"
def __init__(self, name="nand4_dec", height=None):
design.design.__init__(self, name)
super().__init__(name)
self.width = nand4_dec.width
self.height = nand4_dec.height
self.pin_map = nand4_dec.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
# FIXME: For now...

View File

@ -10,7 +10,6 @@ import debug
import utils
from tech import GDS, layer, parameter, drc
from tech import cell_properties as props
from globals import OPTS
import logical_effort
@ -28,12 +27,24 @@ class sense_amp(design.design):
props.sense_amp.pin.vdd,
props.sense_amp.pin.gnd]
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width, height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"])
else:
(width, height) = (0, 0)
pin_map = []
cell_size_layer = "boundary"
def __init__(self, name="sense_amp"):
super().__init__(name)
debug.info(2, "Create sense_amp")
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def get_bl_names(self):
return props.sense_amp.pin.bl
@ -49,15 +60,6 @@ class sense_amp(design.design):
def en_name(self):
return props.sense_amp.pin.en
def __init__(self, name):
super().__init__(name)
debug.info(2, "Create sense_amp")
self.width = sense_amp.width
self.height = sense_amp.height
self.pin_map = sense_amp.pin_map
self.add_pin_types(self.type_list)
def get_cin(self):
# FIXME: This input load will be applied to both the s_en timing and bitline timing.

View File

@ -8,7 +8,8 @@
import debug
import design
import utils
from tech import GDS,layer
from tech import GDS, layer
class tri_gate(design.design):
"""
@ -19,8 +20,7 @@ class tri_gate(design.design):
pin_names = ["in", "out", "en", "en_bar", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"])
cell_size_layer = "boundary"
unique_id = 1
@ -28,12 +28,20 @@ class tri_gate(design.design):
if name=="":
name = "tri{0}".format(tri_gate.unique_id)
tri_gate.unique_id += 1
design.design.__init__(self, name)
super().__init__(self, name)
debug.info(2, "Create tri_gate")
self.width = tri_gate.width
self.height = tri_gate.height
self.pin_map = tri_gate.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def analytical_power(self, corner, load):

View File

@ -8,10 +8,10 @@
import debug
import design
import utils
from globals import OPTS
from tech import GDS,layer
from tech import GDS, layer
from tech import cell_properties as props
class write_driver(design.design):
"""
Tristate write driver to be active during write operations only.
@ -28,20 +28,23 @@ class write_driver(design.design):
props.write_driver.pin.gnd]
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"])
else:
(width,height) = (0,0)
pin_map = []
cell_size_layer = "boundary"
def __init__(self, name):
design.design.__init__(self, name)
super().__init__(name)
debug.info(2, "Create write_driver")
self.width = write_driver.width
self.height = write_driver.height
self.pin_map = write_driver.pin_map
(width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"],
layer[self.cell_size_layer])
pin_map = utils.get_libcell_pins(self.pin_names,
self.cell_name,
GDS["unit"])
self.width = width
self.height = height
self.pin_map = pin_map
self.add_pin_types(self.type_list)
def get_bl_names(self):

View File

@ -9,6 +9,7 @@ import os
import inspect
import globals
import sys
import pdb
# the debug levels:
# 0 = minimum output (default)
@ -26,9 +27,9 @@ def check(check, str):
log("ERROR: file {0}: line {1}: {2}\n".format(
os.path.basename(filename), line_number, str))
if globals.OPTS.debug_level > 0:
import pdb
if globals.OPTS.debug:
pdb.set_trace()
assert 0
@ -40,9 +41,9 @@ def error(str, return_value=0):
log("ERROR: file {0}: line {1}: {2}\n".format(
os.path.basename(filename), line_number, str))
if globals.OPTS.debug_level > 0 and return_value != 0:
import pdb
if globals.OPTS.debug:
pdb.set_trace()
assert return_value == 0
@ -96,7 +97,7 @@ log.create_file = True
def info(lev, str):
from globals import OPTS
if (OPTS.debug_level >= lev):
if (OPTS.verbose_level >= lev):
frm = inspect.stack()[1]
mod = inspect.getmodule(frm[0])
# classname = frm.f_globals['__name__']

View File

@ -2,7 +2,7 @@ word_size = 32
num_words = 128
tech_name = "scn4m_subm"
nominal_corners_only = False
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]

View File

@ -6,7 +6,7 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = False
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]

View File

@ -0,0 +1,21 @@
word_size = 4
num_words = 16
write_size = 2
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 1
tech_name = "scn4m_subm"
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = False
check_lvsdrc = True
output_path = "temp"
output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)

View File

@ -7,7 +7,7 @@ num_r_ports = 0
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = False
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]

View File

@ -6,7 +6,7 @@ num_r_ports = 1
num_w_ports = 1
tech_name = "scn4m_subm"
nominal_corners_only = False
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]

View File

@ -0,0 +1,21 @@
word_size = 2
num_words = 16
num_rw_ports = 2
num_r_ports = 0
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = False
check_lvsdrc = True
output_path = "temp"
output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)

View File

@ -2,14 +2,14 @@ word_size = 2
num_words = 16
tech_name = "freepdk45"
nominal_corners_only = False
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [1.0]
temperatures = [25]
route_supplies = False
check_lvsdrc = True
# nominal_corners_only = True
# nominal_corner_only = True
load_scales = [0.5, 1, 4]
slew_scales = [0.5, 1]

View File

@ -2,7 +2,7 @@ word_size = 2
num_words = 16
tech_name = "scn4m_subm"
nominal_corners_only = False
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]

View File

@ -2,7 +2,7 @@ word_size = 64
num_words = 1024
tech_name = "scn4m_subm"
nominal_corners_only = False
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [ 5.0 ]
temperatures = [ 25 ]

View File

@ -2,7 +2,7 @@ word_size = 16
num_words = 256
tech_name = "scn4m_subm"
nominal_corners_only = False
nominal_corner_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]

View File

@ -9,7 +9,7 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "freepdk45"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = False
check_lvsdrc = False

View File

@ -9,7 +9,7 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = False
check_lvsdrc = False

View File

@ -7,7 +7,7 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = True
check_lvsdrc = True

View File

@ -7,7 +7,7 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = True
check_lvsdrc = True

View File

@ -9,7 +9,7 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = False
check_lvsdrc = False

View File

@ -7,7 +7,7 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = True
check_lvsdrc = True

View File

@ -9,7 +9,7 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "scn4m_subm"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = False
check_lvsdrc = False

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 256
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 0
tech_name = "sky130"
nominal_corner_only = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -9,11 +9,11 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "sky130"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 512
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 0
tech_name = "sky130"
nominal_corner_only = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -9,11 +9,11 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "sky130"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,

View File

@ -0,0 +1,26 @@
word_size = 32
num_words = 1024
write_size = 8
local_array_size = 16
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 0
tech_name = "sky130"
nominal_corner_only = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)
output_name = "sram_1rw_{0}_{1}_{2}_{3}".format(word_size,
num_words,
write_size,
tech_name)

View File

@ -9,11 +9,11 @@ num_r_ports = 1
num_w_ports = 0
tech_name = "sky130"
nominal_corners_only = True
nominal_corner_only = True
route_supplies = True
route_supplies = False
check_lvsdrc = True
perimeter_pins = True
perimeter_pins = False
#netlist_only = True
#analytical_delay = False
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,

View File

@ -1,19 +0,0 @@
word_size = 2
num_words = 16
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
#netlist_only = True
route_supplies = True
check_lvsdrc = True
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,19 +0,0 @@
word_size = 8
num_words = 128
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = True
check_lvsdrc = True
netlist_only = True
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,18 +0,0 @@
word_size = 16
num_words = 256
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = True
check_lvsdrc = True
netlist_only = True
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,18 +0,0 @@
word_size = 32
num_words = 128
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = True
check_lvsdrc = True
netlist_only = True
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,19 +0,0 @@
word_size = 64
num_words = 128
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
route_supplies = True
check_lvsdrc = True
output_path = "/home/jesse/thesis/outputs/run5"
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -1,31 +0,0 @@
word_size = 16
num_words = 16
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 0
tech_name = "sky130"
accuracy_requirement = 0.05
magic_exe = ("magic", "magic")
nominal_corners_only = False
process_corners = ["TT"]
supply_voltages = [5.0]
temperatures = [25]
netlist_only = False
route_supplies = "grid"
check_lvsdrc = False
#replica_bitcell_array = "/home/jesse/openram/technology/sky130/modules/replica_bitcell_array.py"
output_path = "sram_" + str(accuracy_requirement)
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,
num_words,
tech_name,
accuracy_requirement
)
write_size=8

View File

@ -63,7 +63,7 @@ def parse_args():
optparse.make_option("-v",
"--verbose",
action="count",
dest="debug_level",
dest="verbose_level",
help="Increase the verbosity level"),
optparse.make_option("-t",
"--tech",
@ -83,11 +83,16 @@ def parse_args():
action="store_false",
dest="analytical_delay",
help="Perform characterization to calculate delays (default is analytical models)"),
optparse.make_option("-k",
"--keeptemp",
action="store_true",
dest="keep_temp",
help="Keep the contents of the temp directory after a successful run"),
optparse.make_option("-d",
"--dontpurge",
action="store_false",
dest="purge_temp",
help="Don't purge the contents of the temp directory after a successful run")
"--debug",
action="store_true",
dest="debug",
help="Run in debug mode to drop to pdb on failure")
# -h --help is implicit.
}
@ -143,7 +148,7 @@ def check_versions():
major_required = 3
minor_required = 5
if not (major_python_version == major_required and minor_python_version >= minor_required):
debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1)
debug.error("Python {0}.{1} or greater is required.".format(major_required, minor_required), -1)
# FIXME: Check versions of other tools here??
# or, this could be done in each module (e.g. verify, characterizer, etc.)
@ -152,7 +157,7 @@ def check_versions():
try:
import coverage
OPTS.coverage = 1
except:
except ModuleNotFoundError:
OPTS.coverage = 0
@ -189,6 +194,9 @@ def init_openram(config_file, is_unit_test=True):
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
return
# Setup correct bitcell names
setup_bitcell()
# Import these to find the executables for checkpointing
import characterizer
import verify
@ -202,17 +210,12 @@ def setup_bitcell():
"""
Determine the correct custom or parameterized bitcell for the design.
"""
global OPTS
# If we have non-1rw ports,
# and the user didn't over-ride the bitcell manually,
# figure out the right bitcell to use
if (OPTS.bitcell == "bitcell"):
if OPTS.bitcell == "bitcell":
if (OPTS.num_rw_ports == 1 and OPTS.num_w_ports == 0 and OPTS.num_r_ports == 0):
OPTS.bitcell = "bitcell"
OPTS.replica_bitcell = "replica_bitcell"
OPTS.dummy_bitcell = "dummy_bitcell"
else:
ports = ""
if OPTS.num_rw_ports > 0:
@ -226,6 +229,13 @@ def setup_bitcell():
OPTS.bitcell_suffix = "_" + ports
OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix
OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
elif OPTS.bitcell == "pbitcell":
OPTS.bitcell = "pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
# See if bitcell exists
try:
__import__(OPTS.bitcell)
@ -234,6 +244,8 @@ def setup_bitcell():
# or its custom replica bitcell
# Use the pbitcell (and give a warning if not in unit test mode)
OPTS.bitcell = "pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
if not OPTS.is_unit_test:
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
debug.info(1, "Using bitcell: {}".format(OPTS.bitcell))
@ -250,8 +262,8 @@ def get_tool(tool_type, preferences, default_name=None):
if default_name:
exe_name = find_exe(default_name)
if exe_name == None:
debug.error("{0} not found. Cannot find {1} tool.".format(default_name,
tool_type),
debug.error("{0} not found. Cannot find {1} tool.".format(default_name, tool_type)
+ "Disable DRC/LVS with check_lvsdrc=False to ignore.",
2)
else:
debug.info(1, "Using {0}: {1}".format(tool_type, exe_name))
@ -264,8 +276,7 @@ def get_tool(tool_type, preferences, default_name=None):
return(name, exe_name)
else:
debug.info(1,
"Could not find {0}, trying next {1} tool.".format(name,
tool_type))
"Could not find {0}, trying next {1} tool.".format(name, tool_type))
else:
return(None, "")
@ -303,7 +314,7 @@ def read_config(config_file, is_unit_test=True):
try:
config = importlib.import_module(module_name)
except:
debug.error("Unable to read configuration file: {0}".format(config_file),2)
debug.error("Unable to read configuration file: {0}".format(config_file), 2)
OPTS.overridden = {}
for k, v in config.__dict__.items():
@ -360,7 +371,7 @@ def cleanup_paths():
We should clean up the temp directory after execution.
"""
global OPTS
if not OPTS.purge_temp:
if OPTS.keep_temp:
debug.info(0,
"Preserving temp directory: {}".format(OPTS.openram_temp))
return
@ -458,7 +469,7 @@ def set_default_corner():
if OPTS.nominal_corner_only:
OPTS.process_corners = ["TT"]
else:
OPTS.process_corners = tech.spice["fet_models"].keys()
OPTS.process_corners = list(tech.spice["fet_models"].keys())
if (OPTS.supply_voltages == ""):
if OPTS.nominal_corner_only:
@ -528,12 +539,12 @@ def print_time(name, now_time, last_time=None, indentation=2):
global OPTS
# Don't print during testing
if not OPTS.is_unit_test or OPTS.debug_level > 0:
if not OPTS.is_unit_test or OPTS.verbose_level > 0:
if last_time:
time = str(round((now_time-last_time).total_seconds(),1)) + " seconds"
time = str(round((now_time - last_time).total_seconds(), 1)) + " seconds"
else:
time = now_time.strftime('%m/%d/%Y %H:%M:%S')
debug.print_raw("{0} {1}: {2}".format("*"*indentation, name, time))
debug.print_raw("{0} {1}: {2}".format("*" * indentation, name, time))
def report_status():
@ -600,4 +611,4 @@ def report_status():
if OPTS.trim_netlist:
debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).")
if OPTS.nominal_corner_only:
debug.print_raw("Only characterizing nominal corner.")
debug.print_raw("Only generating nominal corner timing.")

View File

@ -9,9 +9,10 @@ import debug
import design
from sram_factory import factory
from math import log, ceil, floor
from tech import drc, layer
from tech import drc
from vector import vector
from globals import OPTS
from tech import layer_properties as layer_props
class bank(design.design):
@ -906,18 +907,13 @@ class bank(design.design):
offset=mid2)
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if OPTS.tech_name == "sky130":
stack = self.m2_stack
pitch = self.m3_pitch
else:
stack = self.m1_stack
pitch = self.m2_pitch
stack = getattr(self, layer_props.bank.stack)
pitch = getattr(self, layer_props.bank.pitch)
if self.col_addr_size == 1:

View File

@ -52,7 +52,7 @@ class bitcell_array(bitcell_base_array):
def add_modules(self):
""" Add the modules used in this design """
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
self.add_mod(self.cell)
def create_instances(self):

View File

@ -9,6 +9,7 @@ import debug
import design
from tech import cell_properties
from sram_factory import factory
from globals import OPTS
class bitcell_base_array(design.design):
@ -24,7 +25,7 @@ class bitcell_base_array(design.design):
self.column_offset = column_offset
# Bitcell for port names only
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
self.wordline_names = [[] for port in self.all_ports]
self.all_wordline_names = []

View File

@ -51,7 +51,7 @@ class col_cap_array(bitcell_base_array):
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
self.add_mod(self.dummy_cell)
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
def create_instances(self):
""" Create the module instances used in this design """

View File

@ -11,7 +11,8 @@ from tech import layer, preferred_directions
from vector import vector
from sram_factory import factory
from globals import OPTS
from tech import cell_properties
from tech import cell_properties as cell_props
from tech import layer_properties as layer_props
class column_mux_array(design.design):
@ -33,14 +34,18 @@ class column_mux_array(design.design):
self.bitcell_br = bitcell_br
self.column_offset = column_offset
if OPTS.tech_name == "sky130":
self.sel_layer = "m3"
self.sel_pitch = self.m3_pitch
self.bitline_layer = "m1"
else:
self.sel_layer = "m1"
self.sel_pitch = self.m2_pitch
self.bitline_layer = "m2"
self.sel_layer = layer_props.column_mux_array.select_layer
self.sel_pitch = getattr(self, layer_props.column_mux_array.select_pitch)
self.bitline_layer = layer_props.column_mux_array.bitline_layer
# if OPTS.tech_name == "sky130":
# self.sel_layer = "m3"
# self.sel_pitch = self.m3_pitch
# self.bitline_layer = "m1"
# else:
# self.sel_layer = "m1"
# self.sel_pitch = self.m2_pitch
# self.bitline_layer = "m2"
if preferred_directions[self.sel_layer] == "V":
self.via_directions = ("H", "H")
@ -123,7 +128,7 @@ class column_mux_array(design.design):
# For every column, add a pass gate
for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
if cell_properties.bitcell.mirror.y and (col_num + self.column_offset) % 2:
if cell_props.bitcell.mirror.y and (col_num + self.column_offset) % 2:
mirror = "MY"
xoffset = xoffset + self.mux.width
else:

View File

@ -85,8 +85,8 @@ class control_logic(design.design):
def add_modules(self):
""" Add all the required modules """
dff = factory.create(module_type="dff_buf")
dff_height = dff.height
self.dff = factory.create(module_type="dff_buf")
dff_height = self.dff.height
self.ctrl_dff_array = factory.create(module_type="dff_buf_array",
rows=self.num_control_signals,
@ -119,7 +119,7 @@ class control_logic(design.design):
# We will use the maximum since this same value is used to size the wl_en
# and the p_en_bar drivers
max_fanout = max(self.num_rows, self.num_cols)
# max_fanout = max(self.num_rows, self.num_cols)
# wl_en drives every row in the bank
self.wl_en_driver = factory.create(module_type="pdriver",
@ -721,10 +721,8 @@ class control_logic(design.design):
def route_supply(self):
""" Add vdd and gnd to the instance cells """
if OPTS.tech_name == "sky130":
supply_layer = "li"
else:
supply_layer = "m1"
supply_layer = self.dff.get_pin("vdd").layer
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")

View File

@ -1,271 +0,0 @@
# 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 design
import debug
from tech import drc
from sram_factory import factory
from vector import vector
from globals import OPTS
class custom_cell(design.design):
"""
Array of tristate drivers to write to the bitlines through the column mux.
Dynamically generated write driver array of all bitlines.
"""
def __init__(self, name, pins, mod):
design.design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("columns: {0}".format(columns))
self.add_comment("word_size {0}".format(word_size))
self.columns = columns
self.word_size = word_size
self.write_size = write_size
self.column_offset = column_offset
self.words_per_row = int(columns / word_size)
if not num_spare_cols:
self.num_spare_cols = 0
else:
self.num_spare_cols = num_spare_cols
if self.write_size:
self.num_wmasks = int(self.word_size / self.write_size)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self):
bl_name = "bl"
return bl_name
def get_br_name(self):
br_name = "br"
return br_name
@property
def data_name(self):
return "data"
@property
def en_name(self):
return "en"
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_write_array()
def create_layout(self):
if self.bitcell.width > self.driver.width:
self.width = (self.columns + self.num_spare_cols) * self.bitcell.width
self.width_regular_cols = self.columns * self.bitcell.width
self.single_col_width = self.bitcell.width
else:
self.width = (self.columns + self.num_spare_cols) * self.driver.width
self.width_regular_cols = self.columns * self.driver.width
self.single_col_width = self.driver.width
self.height = self.driver.height
self.place_write_array()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
for i in range(self.word_size + self.num_spare_cols):
self.add_pin(self.data_name + "_{0}".format(i), "INPUT")
for i in range(self.word_size + self.num_spare_cols):
self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT")
self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT")
if self.write_size:
for i in range(self.num_wmasks + self.num_spare_cols):
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
elif self.num_spare_cols and not self.write_size:
for i in range(self.num_spare_cols + 1):
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
else:
self.add_pin(self.en_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.driver = factory.create(module_type="write_driver")
self.add_mod(self.driver)
# This is just used for measurements,
# so don't add the module
self.bitcell = factory.create(module_type="bitcell")
def create_write_array(self):
self.driver_insts = {}
w = 0
windex=0
for i in range(0, self.columns, self.words_per_row):
name = "write_driver{}".format(i)
index = int(i / self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
if self.write_size:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(windex), "vdd", "gnd"])
w+=1
# when w equals write size, the next en pin can be connected since we are now at the next wmask bit
if w == self.write_size:
w = 0
windex+=1
elif self.num_spare_cols and not self.write_size:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(0), "vdd", "gnd"])
else:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name, "vdd", "gnd"])
for i in range(self.num_spare_cols):
index = self.word_size + i
if self.write_size:
offset = self.num_wmasks
else:
offset = 1
name = "write_driver{}".format(self.columns + i)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(i + offset), "vdd", "gnd"])
def place_write_array(self):
from tech import cell_properties
if self.bitcell.width > self.driver.width:
self.driver_spacing = self.bitcell.width
else:
self.driver_spacing = self.driver.width
for i in range(0, self.columns, self.words_per_row):
index = int(i / self.words_per_row)
xoffset = i * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
mirror = "MY"
xoffset = xoffset + self.driver.width
else:
mirror = ""
base = vector(xoffset, 0)
self.driver_insts[index].place(offset=base, mirror=mirror)
# place spare write drivers (if spare columns are specified)
for i in range(self.num_spare_cols):
index = self.word_size + i
xoffset = (self.columns + i) * self.driver_spacing
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
mirror = "MY"
xoffset = xoffset + self.driver.width
else:
mirror = ""
base = vector(xoffset, 0)
self.driver_insts[index].place(offset=base, mirror=mirror)
def add_layout_pins(self):
for i in range(self.word_size + self.num_spare_cols):
inst = self.driver_insts[i]
din_pin = inst.get_pin(inst.mod.din_name)
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
bl_pin = inst.get_pin(inst.mod.get_bl_names())
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
layer=bl_pin.layer,
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
br_pin = inst.get_pin(inst.mod.get_br_names())
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
layer=br_pin.layer,
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
for n in ["vdd", "gnd"]:
pin_list = self.driver_insts[i].get_pins(n)
for pin in pin_list:
self.add_power_pin(name=n,
loc=pin.center(),
directions=("V", "V"),
start_layer=pin.layer)
if self.write_size:
for bit in range(self.num_wmasks):
inst = self.driver_insts[bit * self.write_size]
en_pin = inst.get_pin(inst.mod.en_name)
# Determine width of wmask modified en_pin with/without col mux
wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
if (self.words_per_row == 1):
en_gap = self.driver_spacing - en_pin.width()
else:
en_gap = self.driver_spacing
self.add_layout_pin(text=self.en_name + "_{0}".format(bit),
layer=en_pin.layer,
offset=en_pin.ll(),
width=wmask_en_len - en_gap,
height=en_pin.height())
for i in range(self.num_spare_cols):
inst = self.driver_insts[self.word_size + i]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks),
layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
elif self.num_spare_cols and not self.write_size:
# shorten enable rail to accomodate those for spare write drivers
inst = self.driver_insts[0]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(0),
layer="m1",
offset=en_pin.ll(),
width=self.width_regular_cols - self.words_per_row * en_pin.width())
# individual enables for every spare write driver
for i in range(self.num_spare_cols):
inst = self.driver_insts[self.word_size + i]
en_pin = inst.get_pin(inst.mod.en_name)
self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1),
layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
else:
inst = self.driver_insts[0]
self.add_layout_pin(text=self.en_name,
layer="m1",
offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1),
width=self.width)
def get_w_en_cin(self):
"""Get the relative capacitance of all the enable connections in the bank"""
# The enable is connected to a nand2 for every row.
return self.driver.get_w_en_cin() * len(self.driver_insts)

View File

@ -7,7 +7,7 @@
#
import debug
import design
from tech import parameter, layer
from tech import layer
from tech import cell_properties as props
from vector import vector
from globals import OPTS
@ -23,7 +23,6 @@ class dff_buf(design.design):
unique_id = 1
def __init__(self, inv1_size=2, inv2_size=4, name=""):
if name=="":
name = "dff_buf_{0}".format(dff_buf.unique_id)
dff_buf.unique_id += 1

View File

@ -44,8 +44,8 @@ class dummy_array(bitcell_base_array):
def add_modules(self):
""" Add the modules used in this design """
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
self.cell = factory.create(module_type="bitcell")
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
self.cell = factory.create(module_type=OPTS.bitcell)
self.add_mod(self.dummy_cell)
def create_instances(self):

View File

@ -11,6 +11,7 @@ import math
from sram_factory import factory
from vector import vector
from globals import OPTS
from tech import layer_properties as layer_props
class hierarchical_decoder(design.design):
@ -26,7 +27,7 @@ class hierarchical_decoder(design.design):
self.pre3x8_inst = []
self.pre4x16_inst = []
b = factory.create(module_type="bitcell")
b = factory.create(module_type=OPTS.bitcell)
self.cell_height = b.height
self.num_outputs = num_outputs
@ -181,24 +182,14 @@ class hierarchical_decoder(design.design):
# Inputs to cells are on input layer
# Outputs from cells are on output layer
if OPTS.tech_name == "sky130":
self.bus_layer = "m1"
self.bus_directions = "nonpref"
self.bus_pitch = self.m1_pitch
self.bus_space = self.m2_space
self.input_layer = "m2"
self.output_layer = "li"
self.output_layer_pitch = self.li_pitch
else:
self.bus_layer = "m2"
self.bus_directions = "pref"
self.bus_pitch = self.m2_pitch
self.bus_space = self.m2_space
# These two layers being the same requires a special jog
# to ensure to conflicts with the output layers
self.input_layer = "m1"
self.output_layer = "m3"
self.output_layer_pitch = self.m3_pitch
self.bus_layer = layer_props.hierarchical_decoder.bus_layer
self.bus_directions = layer_props.hierarchical_decoder.bus_directions
self.bus_pitch = getattr(self, self.bus_layer + "_pitch")
self.bus_space = getattr(self, self.bus_layer + "_space")
self.input_layer = layer_props.hierarchical_decoder.input_layer
self.output_layer = layer_props.hierarchical_decoder.output_layer
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
# Two extra pitches between modules on left and right
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
@ -606,7 +597,7 @@ class hierarchical_decoder(design.design):
must-connects next level up.
"""
if OPTS.tech_name == "sky130":
if layer_props.hierarchical_decoder.vertical_supply:
for n in ["vdd", "gnd"]:
pins = self.and_inst[0].get_pins(n)
for pin in pins:
@ -678,9 +669,9 @@ class hierarchical_decoder(design.design):
mid_point2 = vector(x_offset, y_offset)
rail_pos = vector(self.predecode_bus[rail_name].cx(), mid_point2.y)
self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos])
if OPTS.tech_name == "sky130":
above_rail = vector(self.predecode_bus[rail_name].cx(), mid_point2.y + (self.cell_height/2))
self.add_path(self.bus_layer, [rail_pos, above_rail], width = self.li_width + self.m1_enclose_mcon * 2)
if layer_props.hierarchical_decoder.vertical_supply:
above_rail = vector(self.predecode_bus[rail_name].cx(), mid_point2.y + (self.cell_height / 2))
self.add_path(self.bus_layer, [rail_pos, above_rail], width=self.li_width + self.m1_enclose_mcon * 2)
# pin_pos = pin.center()
# rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y)

View File

@ -11,6 +11,7 @@ import math
from vector import vector
from sram_factory import factory
from globals import OPTS
from tech import layer_properties as layer_props
class hierarchical_predecode(design.design):
@ -20,7 +21,7 @@ class hierarchical_predecode(design.design):
def __init__(self, name, input_number, height=None):
self.number_of_inputs = input_number
b = factory.create(module_type="bitcell")
b = factory.create(module_type=OPTS.bitcell)
if not height:
self.cell_height = b.height
@ -83,23 +84,14 @@ class hierarchical_predecode(design.design):
# Inputs to cells are on input layer
# Outputs from cells are on output layer
if OPTS.tech_name == "sky130":
self.bus_layer = "m1"
self.bus_directions = "nonpref"
self.bus_pitch = self.m1_pitch
self.bus_space = 1.5 * self.m1_space
self.input_layer = "m2"
self.output_layer = "li"
self.output_layer_pitch = self.li_pitch
else:
self.bus_layer = "m2"
self.bus_directions = "pref"
self.bus_pitch = self.m2_pitch
self.bus_space = self.m2_space
# This requires a special jog to ensure to conflicts with the output layers
self.input_layer = "m1"
self.output_layer = "m1"
self.output_layer_pitch = self.m1_pitch
self.bus_layer = layer_props.hierarchical_predecode.bus_layer
self.bus_directions = layer_props.hierarchical_predecode.bus_directions
self.bus_pitch = getattr(self, self.bus_layer + "_pitch")
self.bus_space = layer_props.hierarchical_predecode.bus_space_factor * getattr(self, self.bus_layer + "_space")
self.input_layer = layer_props.hierarchical_predecode.input_layer
self.output_layer = layer_props.hierarchical_predecode.output_layer
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
self.height = self.number_of_outputs * self.and_mod.height
@ -276,9 +268,9 @@ class hierarchical_predecode(design.design):
height=via.mod.second_layer_height,
width=via.mod.second_layer_width)
if OPTS.tech_name == "sky130":
below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height/2))
self.add_path(self.bus_layer, [rail_pos, below_rail], width = self.li_width + self.m1_enclose_mcon * 2)
if layer_props.hierarchical_predecode.vertical_supply:
below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2))
self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2)
def route_and_to_rails(self):
# This 2D array defines the connection mapping
@ -319,8 +311,8 @@ class hierarchical_predecode(design.design):
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
# In sky130, we use hand-made decoder cells with vertical power
if OPTS.tech_name == "sky130" and not self.column_decoder:
# We may ahve vertical power supply rails
if layer_props.hierarchical_predecode.vertical_supply and not self.column_decoder:
for n in ["vdd", "gnd"]:
# This makes a wire from top to bottom for both inv and and gates
for i in [self.inv_inst, self.and_inst]:

View File

@ -64,7 +64,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
def add_modules(self):
""" Add the modules used in this design """
# This is just used for names
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
cols=self.cols,

View File

@ -46,7 +46,7 @@ class bitcell_array(bitcell_base_array):
def add_modules(self):
""" Add the modules used in this design """
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
self.add_mod(self.cell)
def create_instances(self):

View File

@ -10,6 +10,7 @@ from sram_factory import factory
from vector import vector
from tech import layer
from globals import OPTS
from tech import layer_properties as layer_props
class port_address(design.design):
@ -80,7 +81,7 @@ class port_address(design.design):
self.copy_power_pins(inst, "gnd")
for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"):
if OPTS.tech_name == "sky130":
if layer_props.port_address.supply_offset:
self.add_power_pin("vdd", rbl_vdd_pin.center())
else:
self.add_power_pin("vdd", rbl_vdd_pin.lc())
@ -154,7 +155,7 @@ class port_address(design.design):
# The polarity must be switched if we have a hierarchical wordline
# to compensate for the local array inverters
b = factory.create(module_type="bitcell")
b = factory.create(module_type=OPTS.bitcell)
if local_array_size > 0:
self.rbl_driver = factory.create(module_type="inv_dec",

View File

@ -11,6 +11,7 @@ from sram_factory import factory
from collections import namedtuple
from vector import vector
from globals import OPTS
from tech import layer_properties as layer_props
class port_data(design.design):
@ -32,7 +33,7 @@ class port_data(design.design):
self.num_spare_cols = 0
if not bit_offsets:
bitcell = factory.create(module_type="bitcell")
bitcell = factory.create(module_type=OPTS.bitcell)
self.bit_offsets = []
for i in range(self.num_cols + self.num_spare_cols):
self.bit_offsets.append(i * bitcell.width)
@ -190,7 +191,7 @@ class port_data(design.design):
# and mirroring happens correctly
# Used for names/dimensions only
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
if self.port == 0:
# Append an offset on the left
@ -269,7 +270,7 @@ class port_data(design.design):
# create arrays of bitline and bitline_bar names for read,
# write, or all ports
self.bitcell = factory.create(module_type="bitcell")
self.bitcell = factory.create(module_type=OPTS.bitcell)
self.bl_names = self.bitcell.get_all_bl_names()
self.br_names = self.bitcell.get_all_br_names()
self.wl_names = self.bitcell.get_all_wl_names()
@ -580,20 +581,20 @@ class port_data(design.design):
inst1_start_bit=self.num_cols + off,
inst2_start_bit=self.word_size)
# This could be a channel route, but in some techs the bitlines
# are too close together.
elif OPTS.tech_name == "sky130":
self.connect_bitlines(inst1=inst1,
inst1_bls_template=inst1_bls_templ,
inst2=inst2,
num_bits=self.word_size,
inst1_start_bit=start_bit)
else:
elif layer_props.port_data.channel_route_bitlines:
self.channel_route_bitlines(inst1=inst1,
inst1_bls_template=inst1_bls_templ,
inst2=inst2,
num_bits=self.word_size + self.num_spare_cols,
inst1_start_bit=start_bit)
# This could be a channel route, but in some techs the bitlines
# are too close together.
else:
self.connect_bitlines(inst1=inst1,
inst1_bls_template=inst1_bls_templ,
inst2=inst2,
num_bits=self.word_size,
inst1_start_bit=start_bit)
def route_write_driver_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """
@ -640,17 +641,16 @@ class port_data(design.design):
# This could be a channel route, but in some techs the bitlines
# are too close together.
elif OPTS.tech_name == "sky130":
elif layer_props.port_data.channel_route_bitlines:
self.channel_route_bitlines(inst1=inst1, inst2=inst2,
num_bits=self.word_size + self.num_spare_cols,
inst1_bls_template=inst1_bls_templ,
inst1_start_bit=start_bit)
else:
self.connect_bitlines(inst1=inst1, inst2=inst2,
num_bits=self.word_size,
inst1_bls_template=inst1_bls_templ,
inst1_start_bit=start_bit)
else:
self.channel_route_bitlines(inst1=inst1, inst2=inst2,
num_bits=self.word_size+self.num_spare_cols,
inst1_bls_template=inst1_bls_templ,
inst1_start_bit=start_bit)
def route_write_driver_to_sense_amp(self, port):
""" Routing of BL and BR between write driver and sense amp """

View File

@ -281,7 +281,7 @@ class replica_bitcell_array(bitcell_base_array):
self.supplies = ["vdd", "gnd"]
# Used for names/dimensions only
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
# Main array
self.bitcell_array_inst=self.add_inst(name="bitcell_array",

View File

@ -9,6 +9,7 @@ from tech import cell_properties
from sram_factory import factory
from vector import vector
from globals import OPTS
from tech import layer_properties as layer_props
class replica_column(bitcell_base_array):
@ -41,7 +42,7 @@ class replica_column(bitcell_base_array):
"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":
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.")
@ -76,9 +77,9 @@ class replica_column(bitcell_base_array):
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.replica_cell = factory.create(module_type="replica_{}".format(OPTS.bitcell))
self.replica_cell = factory.create(module_type=OPTS.replica_bitcell)
self.add_mod(self.replica_cell)
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
self.add_mod(self.dummy_cell)
try:
edge_module_type = ("col_cap" if cell_properties.bitcell.end_caps else "dummy")
@ -87,7 +88,7 @@ class replica_column(bitcell_base_array):
self.edge_cell = factory.create(module_type=edge_module_type + "_" + OPTS.bitcell)
self.add_mod(self.edge_cell)
# Used for pin names only
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
def create_instances(self):
self.cell_inst = {}

View File

@ -43,7 +43,7 @@ class row_cap_array(bitcell_base_array):
self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell))
self.add_mod(self.dummy_cell)
self.cell = factory.create(module_type="bitcell")
self.cell = factory.create(module_type=OPTS.bitcell)
def create_instances(self):
""" Create the module instances used in this design """

View File

@ -97,7 +97,7 @@ class sense_amp_array(design.design):
# This is just used for measurements,
# so don't add the module
self.bitcell = factory.create(module_type="bitcell")
self.bitcell = factory.create(module_type=OPTS.bitcell)
def create_sense_amp_array(self):
self.local_insts = []

View File

@ -7,10 +7,11 @@
#
import debug
import design
from tech import drc, layer
from tech import layer
from vector import vector
from sram_factory import factory
from globals import OPTS
from tech import layer_properties as layer_props
class wordline_buffer_array(design.design):
@ -58,7 +59,7 @@ class wordline_buffer_array(design.design):
self.add_pin("gnd", "GROUND")
def add_modules(self):
b = factory.create(module_type="bitcell")
b = factory.create(module_type=OPTS.bitcell)
self.wl_driver = factory.create(module_type="inv_dec",
size=self.cols,
@ -70,7 +71,7 @@ class wordline_buffer_array(design.design):
Add a pin for each row of vdd/gnd which
are must-connects next level up.
"""
if OPTS.tech_name == "sky130":
if layer_props.wordline_driver.vertical_supply:
for name in ["vdd", "gnd"]:
supply_pins = self.wld_inst[0].get_pins(name)
for pin in supply_pins:

View File

@ -11,6 +11,7 @@ from tech import drc, layer
from vector import vector
from sram_factory import factory
from globals import OPTS
from tech import layer_properties as layer_props
class wordline_driver_array(design.design):
@ -71,7 +72,7 @@ class wordline_driver_array(design.design):
Add a pin for each row of vdd/gnd which
are must-connects next level up.
"""
if OPTS.tech_name == "sky130":
if layer_props.wordline_driver.vertical_supply:
for name in ["vdd", "gnd"]:
supply_pins = self.wld_inst[0].get_pins(name)
for pin in supply_pins:

View File

@ -99,7 +99,7 @@ class write_driver_array(design.design):
# This is just used for measurements,
# so don't add the module
self.bitcell = factory.create(module_type="bitcell")
self.bitcell = factory.create(module_type=OPTS.bitcell)
def create_write_array(self):
self.driver_insts = []

View File

@ -81,7 +81,7 @@ class write_mask_and_array(design.design):
# This ensures the write mask AND array will be directly under the corresponding write driver enable wire.
# This is just used for measurements, so don't add the module
self.bitcell = factory.create(module_type="bitcell")
self.bitcell = factory.create(module_type=OPTS.bitcell)
self.driver = factory.create(module_type="write_driver")
if self.bitcell.width > self.driver.width:
self.driver_spacing = self.bitcell.width

View File

@ -79,7 +79,10 @@ class options(optparse.Values):
os.getpid())
# This is the verbosity level to control debug information. 0 is none, 1
# is minimal, etc.
debug_level = 0
verbose_level = 0
# Drop to pdb on failure?
debug = False
###################
# Run-time vs accuracy options.
@ -137,7 +140,8 @@ class options(optparse.Values):
# Route the input/output pins to the perimeter
perimeter_pins = False
purge_temp = True
keep_temp = False
# These are the default modules that can be over-riden
bitcell_suffix = ""

View File

@ -10,6 +10,7 @@ import debug
from tech import drc, layer
from vector import vector
from sram_factory import factory
from tech import cell_properties as cell_props
from globals import OPTS
@ -64,7 +65,7 @@ class column_mux(pgate.pgate):
self.add_pn_wells()
def add_ptx(self):
self.bitcell = factory.create(module_type="bitcell")
self.bitcell = factory.create(module_type=OPTS.bitcell)
# Adds nmos_lower,nmos_upper to the module
self.ptx_width = self.tx_size * drc("minwidth_tx")
@ -124,8 +125,8 @@ class column_mux(pgate.pgate):
+ vector(0, self.nmos.active_height + max(self.active_space, self.poly_space))
self.nmos_upper.place(nmos_upper_position)
if OPTS.tech_name == "sky130":
self.add_implants()
if cell_props.pgate.add_implants:
self.extend_implants()
def connect_poly(self):
""" Connect the poly gate of the two pass transistors """
@ -198,7 +199,7 @@ class column_mux(pgate.pgate):
self.add_path(self.col_mux_stack[2],
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()])
def add_implants(self):
def extend_implants(self):
"""
Add top-to-bottom implants for adjacency issues in s8.
"""

View File

@ -146,4 +146,3 @@ class pand2(pgate.pgate):
offset=pin.center(),
width=pin.width(),
height=pin.height())

View File

@ -161,4 +161,3 @@ class pand3(pgate.pgate):
slew=nand_delay.slew,
load=load)
return nand_delay + inv_delay

View File

@ -168,4 +168,3 @@ class pdriver(pgate.pgate):
def get_sizes(self):
""" Return the relative sizes of the buffers """
return self.size_list

View File

@ -13,8 +13,8 @@ from bisect import bisect_left
from tech import layer, drc
from vector import vector
from globals import OPTS
if(OPTS.tech_name == "sky130"):
from tech import cell_properties as cell_props
if cell_props.ptx.bin_spice_models:
from tech import nmos_bins, pmos_bins
@ -192,7 +192,7 @@ class pgate(design.design):
width=self.width + 2 * self.well_extend_active,
height=pwell_height)
if OPTS.tech_name == "sky130":
if cell_props.pgate.add_implants:
self.extend_implants()
def add_nwell_contact(self, pmos, pmos_pos):

Some files were not shown because too many files have changed in this diff Show More