mirror of https://github.com/VLSIDA/OpenRAM.git
Merged with dev
This commit is contained in:
commit
84ba5c55d1
|
|
@ -34,7 +34,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
# This will ignore the name parameter since
|
# This will ignore the name parameter since
|
||||||
# we can guarantee a unique name here
|
# we can guarantee a unique name here
|
||||||
|
|
||||||
super().__init__(name)
|
super().__init__(name, name)
|
||||||
debug.info(4, "create contact object {0}".format(name))
|
debug.info(4, "create contact object {0}".format(name))
|
||||||
|
|
||||||
self.add_comment("layers: {0}".format(layer_stack))
|
self.add_comment("layers: {0}".format(layer_stack))
|
||||||
|
|
@ -51,8 +51,8 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
|
|
||||||
# Non-preferred directions
|
# Non-preferred directions
|
||||||
if directions == "nonpref":
|
if directions == "nonpref":
|
||||||
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
|
first_dir = "H" if tech.preferred_directions[layer_stack[0]]=="V" else "V"
|
||||||
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
|
second_dir = "H" if tech.preferred_directions[layer_stack[2]]=="V" else "V"
|
||||||
self.directions = (first_dir, second_dir)
|
self.directions = (first_dir, second_dir)
|
||||||
# Preferred directions
|
# Preferred directions
|
||||||
elif directions == "pref":
|
elif directions == "pref":
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,15 @@
|
||||||
#
|
#
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class _pins:
|
class _pins:
|
||||||
def __init__(self, pin_dict):
|
def __init__(self, pin_dict):
|
||||||
# make the pins elements of the class to allow "." access.
|
# make the pins elements of the class to allow "." access.
|
||||||
# For example: props.bitcell.cell_6t.pin.bl = "foobar"
|
# 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
|
self.__dict__[k] = v
|
||||||
|
|
||||||
|
|
||||||
class _cell:
|
class _cell:
|
||||||
def __init__(self, pin_dict):
|
def __init__(self, pin_dict):
|
||||||
pin_dict.update(self._default_power_pins())
|
pin_dict.update(self._default_power_pins())
|
||||||
|
|
@ -24,13 +26,27 @@ class _cell:
|
||||||
return self._pins
|
return self._pins
|
||||||
|
|
||||||
def _default_power_pins(self):
|
def _default_power_pins(self):
|
||||||
return { 'vdd' : 'vdd', 'gnd' : 'gnd' }
|
return {'vdd': 'vdd',
|
||||||
|
'gnd': 'gnd'}
|
||||||
|
|
||||||
|
|
||||||
class _mirror_axis:
|
class _mirror_axis:
|
||||||
def __init__(self, x, y):
|
def __init__(self, x, y):
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
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:
|
class _bitcell:
|
||||||
def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r):
|
def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r):
|
||||||
self.mirror = mirror
|
self.mirror = mirror
|
||||||
|
|
@ -42,27 +58,27 @@ class _bitcell:
|
||||||
def _default():
|
def _default():
|
||||||
axis = _mirror_axis(True, False)
|
axis = _mirror_axis(True, False)
|
||||||
|
|
||||||
cell_s8_6t = _cell({'bl' : 'bl',
|
cell_s8_6t = _cell({'bl': 'bl',
|
||||||
'br' : 'br',
|
'br': 'br',
|
||||||
'wl': 'wl'})
|
'wl': 'wl'})
|
||||||
|
|
||||||
cell_6t = _cell({'bl' : 'bl',
|
cell_6t = _cell({'bl': 'bl',
|
||||||
'br' : 'br',
|
'br': 'br',
|
||||||
'wl' : 'wl'})
|
'wl': 'wl'})
|
||||||
|
|
||||||
cell_1rw1r = _cell({'bl0' : 'bl0',
|
cell_1rw1r = _cell({'bl0': 'bl0',
|
||||||
'br0' : 'br0',
|
'br0': 'br0',
|
||||||
'bl1' : 'bl1',
|
'bl1': 'bl1',
|
||||||
'br1' : 'br1',
|
'br1': 'br1',
|
||||||
'wl0' : 'wl0',
|
'wl0': 'wl0',
|
||||||
'wl1' : 'wl1'})
|
'wl1': 'wl1'})
|
||||||
|
|
||||||
cell_1w1r = _cell({'bl0' : 'bl0',
|
cell_1w1r = _cell({'bl0': 'bl0',
|
||||||
'br0' : 'br0',
|
'br0': 'br0',
|
||||||
'bl1' : 'bl1',
|
'bl1': 'bl1',
|
||||||
'br1' : 'br1',
|
'br1': 'br1',
|
||||||
'wl0' : 'wl0',
|
'wl0': 'wl0',
|
||||||
'wl1' : 'wl1'})
|
'wl1': 'wl1'})
|
||||||
|
|
||||||
return _bitcell(cell_s8_6t=cell_s8_6t,
|
return _bitcell(cell_s8_6t=cell_s8_6t,
|
||||||
cell_6t=cell_6t,
|
cell_6t=cell_6t,
|
||||||
|
|
@ -94,21 +110,25 @@ class _dff:
|
||||||
self.custom_type_list = custom_type_list
|
self.custom_type_list = custom_type_list
|
||||||
self.clk_pin = clk_pin
|
self.clk_pin = clk_pin
|
||||||
|
|
||||||
|
|
||||||
class _dff_buff:
|
class _dff_buff:
|
||||||
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
|
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
|
||||||
self.use_custom_ports = use_custom_ports
|
self.use_custom_ports = use_custom_ports
|
||||||
self.buf_ports = custom_buff_ports
|
self.buf_ports = custom_buff_ports
|
||||||
self.add_body_contacts = add_body_contacts
|
self.add_body_contacts = add_body_contacts
|
||||||
|
|
||||||
|
|
||||||
class _dff_buff_array:
|
class _dff_buff_array:
|
||||||
def __init__(self, use_custom_ports, add_body_contacts):
|
def __init__(self, use_custom_ports, add_body_contacts):
|
||||||
self.use_custom_ports = use_custom_ports
|
self.use_custom_ports = use_custom_ports
|
||||||
self.add_body_contacts = add_body_contacts
|
self.add_body_contacts = add_body_contacts
|
||||||
|
|
||||||
|
|
||||||
class _bitcell_array:
|
class _bitcell_array:
|
||||||
def __init__(self, use_custom_cell_arrangement):
|
def __init__(self, use_custom_cell_arrangement):
|
||||||
self.use_custom_cell_arrangement = use_custom_cell_arrangement
|
self.use_custom_cell_arrangement = use_custom_cell_arrangement
|
||||||
|
|
||||||
|
|
||||||
class cell_properties():
|
class cell_properties():
|
||||||
"""
|
"""
|
||||||
This contains meta information about the custom designed cells. For
|
This contains meta information about the custom designed cells. For
|
||||||
|
|
@ -117,37 +137,65 @@ class cell_properties():
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.names = {}
|
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._bitcell = _bitcell._default()
|
||||||
|
|
||||||
self._dff = _dff(use_custom_ports = False,
|
self._ptx = _ptx(model_is_subckt=False,
|
||||||
custom_port_list = ["D", "Q", "clk", "vdd", "gnd"],
|
bin_spice_models=False)
|
||||||
custom_type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
|
|
||||||
clk_pin= "clk")
|
|
||||||
|
|
||||||
self._dff_buff = _dff_buff(use_custom_ports = False,
|
self._pgate = _pgate(add_implants=False)
|
||||||
custom_buff_ports = ["D", "qint", "clk", "vdd", "gnd"],
|
|
||||||
add_body_contacts = False)
|
|
||||||
|
|
||||||
self._dff_buff_array = _dff_buff_array(use_custom_ports = False,
|
self._dff = _dff(use_custom_ports=False,
|
||||||
add_body_contacts = 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',
|
self._write_driver = _cell({'din': 'din',
|
||||||
'bl' : 'bl',
|
'bl': 'bl',
|
||||||
'br' : 'br',
|
'br': 'br',
|
||||||
'en' : 'en'})
|
'en': 'en'})
|
||||||
|
|
||||||
self._sense_amp = _cell({'bl' : 'bl',
|
self._sense_amp = _cell({'bl': 'bl',
|
||||||
'br' : 'br',
|
'br': 'br',
|
||||||
'dout' : 'dout',
|
'dout': 'dout',
|
||||||
'en' : 'en'})
|
'en': 'en'})
|
||||||
|
|
||||||
self._bitcell_array = _bitcell_array(use_custom_cell_arrangement = [])
|
self._bitcell_array = _bitcell_array(use_custom_cell_arrangement=[])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bitcell(self):
|
def bitcell(self):
|
||||||
return self._bitcell
|
return self._bitcell
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ptx(self):
|
||||||
|
return self._ptx
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pgate(self):
|
||||||
|
return self._pgate
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dff(self):
|
def dff(self):
|
||||||
return self._dff
|
return self._dff
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -8,22 +8,29 @@
|
||||||
from hierarchy_design import hierarchy_design
|
from hierarchy_design import hierarchy_design
|
||||||
from utils import round_to_grid
|
from utils import round_to_grid
|
||||||
import contact
|
import contact
|
||||||
|
from tech import preferred_directions
|
||||||
|
from tech import cell_properties as props
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import re
|
import re
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
class design(hierarchy_design):
|
class design(hierarchy_design):
|
||||||
"""
|
"""
|
||||||
This is the same as the hierarchy_design class except it contains
|
This is the same as the hierarchy_design class except it contains
|
||||||
some DRC/layer constants and analytical models for other modules to reuse.
|
some DRC/layer constants and analytical models for other modules to reuse.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name=None):
|
||||||
super().__init__(name)
|
# 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()
|
self.setup_multiport_constants()
|
||||||
|
|
||||||
def check_pins(self):
|
def check_pins(self):
|
||||||
|
|
@ -32,7 +39,98 @@ class design(hierarchy_design):
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
print(pin_name, pin)
|
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
|
These are some layer constants used
|
||||||
in many places in the compiler.
|
in many places in the compiler.
|
||||||
|
|
@ -46,7 +144,7 @@ class design(hierarchy_design):
|
||||||
# Set the stack as a local helper
|
# Set the stack as a local helper
|
||||||
try:
|
try:
|
||||||
layer_stack = getattr(tech, key)
|
layer_stack = getattr(tech, key)
|
||||||
setattr(self, key, layer_stack)
|
setattr(design, key, layer_stack)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -55,14 +153,14 @@ class design(hierarchy_design):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Add the pitch
|
# Add the pitch
|
||||||
setattr(self,
|
setattr(design,
|
||||||
"{}_pitch".format(layer),
|
"{}_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)
|
# Add the non-preferrd pitch (which has vias in the "wrong" way)
|
||||||
setattr(self,
|
setattr(design,
|
||||||
"{}_nonpref_pitch".format(layer),
|
"{}_nonpref_pitch".format(layer),
|
||||||
self.compute_pitch(layer, False))
|
design.compute_pitch(layer, False))
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from tech import preferred_directions
|
from tech import preferred_directions
|
||||||
|
|
@ -73,17 +171,18 @@ class design(hierarchy_design):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
print("{0} width {1} space {2}".format(name,
|
print("{0} width {1} space {2}".format(name,
|
||||||
getattr(self, "{}_width".format(name)),
|
getattr(design, "{}_width".format(name)),
|
||||||
getattr(self, "{}_space".format(name))))
|
getattr(design, "{}_space".format(name))))
|
||||||
|
|
||||||
print("pitch {0} nonpref {1}".format(getattr(self, "{}_pitch".format(name)),
|
print("pitch {0} nonpref {1}".format(getattr(design, "{}_pitch".format(name)),
|
||||||
getattr(self, "{}_nonpref_pitch".format(name))))
|
getattr(design, "{}_nonpref_pitch".format(name))))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
import sys
|
import sys
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def compute_pitch(self, layer, preferred=True):
|
@staticmethod
|
||||||
|
def compute_pitch(layer, preferred=True):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This is the preferred direction pitch
|
This is the preferred direction pitch
|
||||||
|
|
@ -95,13 +194,18 @@ class design(hierarchy_design):
|
||||||
for stack in layer_stacks:
|
for stack in layer_stacks:
|
||||||
# Compute the pitch with both vias above and below (if they exist)
|
# Compute the pitch with both vias above and below (if they exist)
|
||||||
if stack[0] == layer:
|
if stack[0] == layer:
|
||||||
pitches.append(self.compute_layer_pitch(stack, preferred))
|
pitches.append(design.compute_layer_pitch(stack, preferred))
|
||||||
if stack[2] == layer:
|
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)
|
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
|
(layer1, via, layer2) = layer_stack
|
||||||
try:
|
try:
|
||||||
|
|
@ -113,16 +217,16 @@ class design(hierarchy_design):
|
||||||
contact1 = getattr(contact, layer2 + "_via")
|
contact1 = getattr(contact, layer2 + "_via")
|
||||||
|
|
||||||
if preferred:
|
if preferred:
|
||||||
if self.get_preferred_direction(layer1) == "V":
|
if preferred_directions[layer1] == "V":
|
||||||
contact_width = contact1.first_layer_width
|
contact_width = contact1.first_layer_width
|
||||||
else:
|
else:
|
||||||
contact_width = contact1.first_layer_height
|
contact_width = contact1.first_layer_height
|
||||||
else:
|
else:
|
||||||
if self.get_preferred_direction(layer1) == "V":
|
if preferred_directions[layer1] == "V":
|
||||||
contact_width = contact1.first_layer_height
|
contact_width = contact1.first_layer_height
|
||||||
else:
|
else:
|
||||||
contact_width = contact1.first_layer_width
|
contact_width = contact1.first_layer_width
|
||||||
layer_space = getattr(self, layer1 + "_space")
|
layer_space = getattr(design, layer1 + "_space")
|
||||||
|
|
||||||
#print(layer_stack)
|
#print(layer_stack)
|
||||||
#print(contact1)
|
#print(contact1)
|
||||||
|
|
@ -130,94 +234,6 @@ class design(hierarchy_design):
|
||||||
|
|
||||||
return round_to_grid(pitch)
|
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):
|
def setup_multiport_constants(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -266,3 +282,6 @@ class design(hierarchy_design):
|
||||||
total_module_power += inst.mod.analytical_power(corner, load)
|
total_module_power += inst.mod.analytical_power(corner, load)
|
||||||
return total_module_power
|
return total_module_power
|
||||||
|
|
||||||
|
design.setup_drc_constants()
|
||||||
|
design.setup_layer_constants()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ class instance(geometry):
|
||||||
self.mod.gds_write_file(self.gds)
|
self.mod.gds_write_file(self.gds)
|
||||||
# now write an instance of my module/structure
|
# now write an instance of my module/structure
|
||||||
new_layout.addInstance(self.gds,
|
new_layout.addInstance(self.gds,
|
||||||
self.mod.name,
|
self.mod.cell_name,
|
||||||
offsetInMicrons=self.offset,
|
offsetInMicrons=self.offset,
|
||||||
mirror=self.mirror,
|
mirror=self.mirror,
|
||||||
rotate=self.rotate)
|
rotate=self.rotate)
|
||||||
|
|
@ -402,11 +402,11 @@ class instance(geometry):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" 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):
|
def __repr__(self):
|
||||||
""" override print function output """
|
""" 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):
|
class path(geometry):
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
"""
|
"""
|
||||||
name_map = []
|
name_map = []
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name):
|
||||||
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds"
|
self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds"
|
||||||
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
|
self.sp_file = OPTS.openram_tech + "sp_lib/" + cell_name + ".sp"
|
||||||
|
|
||||||
# If we have a separate lvs directory, then all the lvs files
|
# If we have a separate lvs directory, then all the lvs files
|
||||||
# should be in there (all or nothing!)
|
# 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 + "/"
|
lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
|
||||||
|
|
||||||
if os.path.exists(lvs_dir):
|
if os.path.exists(lvs_dir):
|
||||||
self.lvs_file = lvs_dir + name + ".sp"
|
self.lvs_file = lvs_dir + cell_name + ".sp"
|
||||||
else:
|
else:
|
||||||
self.lvs_file = self.sp_file
|
self.lvs_file = self.sp_file
|
||||||
|
|
||||||
|
|
@ -41,8 +41,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
self.lvs_errors = "skipped"
|
self.lvs_errors = "skipped"
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
hierarchy_spice.spice.__init__(self, name)
|
self.cell_name = cell_name
|
||||||
hierarchy_layout.layout.__init__(self, name)
|
hierarchy_spice.spice.__init__(self, name, cell_name)
|
||||||
|
hierarchy_layout.layout.__init__(self, name, cell_name)
|
||||||
self.init_graph_params()
|
self.init_graph_params()
|
||||||
|
|
||||||
def get_layout_pins(self, inst):
|
def get_layout_pins(self, inst):
|
||||||
|
|
@ -76,20 +77,20 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
self.lvs_write(tempspice)
|
self.lvs_write(tempspice)
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
# Final verification option does not allow nets to be connected by label.
|
# 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.drc_errors = verify.run_drc(self.cell_name, tempgds, extract=True, final_verification=final_verification)
|
||||||
self.lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, 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
|
# force_check is used to determine decoder height and other things, so we shouldn't fail
|
||||||
# if that flag is set
|
# if that flag is set
|
||||||
if OPTS.inline_lvsdrc and not force_check:
|
if OPTS.inline_lvsdrc and not force_check:
|
||||||
debug.check(self.drc_errors == 0,
|
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))
|
self.drc_errors))
|
||||||
debug.check(self.lvs_errors == 0,
|
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))
|
self.lvs_errors))
|
||||||
|
|
||||||
if OPTS.purge_temp:
|
if not OPTS.keep_temp:
|
||||||
os.remove(tempspice)
|
os.remove(tempspice)
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
|
|
@ -104,14 +105,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
return
|
return
|
||||||
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
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)
|
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,
|
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))
|
num_errors))
|
||||||
|
|
||||||
if OPTS.purge_temp:
|
if not OPTS.keep_temp:
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
def LVS(self, final_verification=False):
|
def LVS(self, final_verification=False):
|
||||||
|
|
@ -125,15 +126,15 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
return
|
return
|
||||||
elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
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)
|
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name)
|
||||||
self.lvs_write(tempspice)
|
self.lvs_write(tempspice)
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
|
||||||
debug.check(num_errors == 0,
|
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))
|
num_errors))
|
||||||
if OPTS.purge_temp:
|
if not OPTS.keep_temp:
|
||||||
os.remove(tempspice)
|
os.remove(tempspice)
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
|
|
@ -217,7 +218,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
pins = ",".join(self.pins)
|
pins = ",".join(self.pins)
|
||||||
insts = [" {}".format(x) for x in self.insts]
|
insts = [" {}".format(x) for x in self.insts]
|
||||||
objs = [" {}".format(x) for x in self.objs]
|
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 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 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))
|
s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts))
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,9 @@ class layout():
|
||||||
layout/netlist and perform LVS/DRC.
|
layout/netlist and perform LVS/DRC.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name, cell_name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.cell_name = cell_name
|
||||||
self.width = None
|
self.width = None
|
||||||
self.height = None
|
self.height = None
|
||||||
self.bounding_box = None
|
self.bounding_box = None
|
||||||
|
|
@ -215,7 +216,7 @@ class layout():
|
||||||
# Contacts are not really instances, so skip them
|
# Contacts are not really instances, so skip them
|
||||||
if "contact" not in mod.name:
|
if "contact" not in mod.name:
|
||||||
# Check that the instance name is unique
|
# 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.inst_names.add(name)
|
||||||
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
|
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
|
||||||
|
|
@ -316,7 +317,7 @@ class layout():
|
||||||
return any_pin
|
return any_pin
|
||||||
except Exception:
|
except Exception:
|
||||||
self.gds_write("missing_pin.gds")
|
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):
|
def get_pins(self, text):
|
||||||
"""
|
"""
|
||||||
|
|
@ -537,10 +538,6 @@ class layout():
|
||||||
position_list=coordinates,
|
position_list=coordinates,
|
||||||
widen_short_wires=widen_short_wires)
|
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):
|
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
|
||||||
""" Add a three layer via structure. """
|
""" Add a three layer via structure. """
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import re
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
import tech
|
import tech
|
||||||
|
from pprint import pformat
|
||||||
from delay_data import delay_data
|
from delay_data import delay_data
|
||||||
from wire_spice_model import wire_spice_model
|
from wire_spice_model import wire_spice_model
|
||||||
from power_data import power_data
|
from power_data import power_data
|
||||||
|
|
@ -26,8 +27,9 @@ class spice():
|
||||||
Class consisting of a set of modules and instances of these modules
|
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.name = name
|
||||||
|
self.cell_name = cell_name
|
||||||
|
|
||||||
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
# Holds subckts/mods for this module
|
# Holds subckts/mods for this module
|
||||||
|
|
@ -164,7 +166,6 @@ class spice():
|
||||||
num_pins = len(self.insts[-1].mod.pins)
|
num_pins = len(self.insts[-1].mod.pins)
|
||||||
num_args = len(args)
|
num_args = len(args)
|
||||||
if (check and num_pins != num_args):
|
if (check and num_pins != num_args):
|
||||||
from pprint import pformat
|
|
||||||
if num_pins < num_args:
|
if num_pins < num_args:
|
||||||
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins)
|
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins)
|
||||||
arg_pins = args
|
arg_pins = args
|
||||||
|
|
@ -181,7 +182,6 @@ class spice():
|
||||||
self.conns.append(args)
|
self.conns.append(args)
|
||||||
|
|
||||||
if check and (len(self.insts)!=len(self.conns)):
|
if check and (len(self.insts)!=len(self.conns)):
|
||||||
from pprint import pformat
|
|
||||||
insts_string=pformat(self.insts)
|
insts_string=pformat(self.insts)
|
||||||
conns_string=pformat(self.conns)
|
conns_string=pformat(self.conns)
|
||||||
|
|
||||||
|
|
@ -214,7 +214,7 @@ class spice():
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# find the correct subckt line in the file
|
# 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]
|
subckt_line = list(filter(subckt.search, self.spice))[0]
|
||||||
# parses line into ports and remove subckt
|
# parses line into ports and remove subckt
|
||||||
self.pins = subckt_line.split(" ")[2:]
|
self.pins = subckt_line.split(" ")[2:]
|
||||||
|
|
@ -234,7 +234,7 @@ class spice():
|
||||||
|
|
||||||
# pins and subckt should be the same
|
# pins and subckt should be the same
|
||||||
# find the correct subckt line in the file
|
# 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]
|
subckt_line = list(filter(subckt.search, self.lvs))[0]
|
||||||
# parses line into ports and remove subckt
|
# parses line into ports and remove subckt
|
||||||
lvs_pins = subckt_line.split(" ")[2:]
|
lvs_pins = subckt_line.split(" ")[2:]
|
||||||
|
|
@ -293,7 +293,7 @@ class spice():
|
||||||
return
|
return
|
||||||
|
|
||||||
# write out the first spice line (the subcircuit)
|
# 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)))
|
" ".join(self.pins)))
|
||||||
|
|
||||||
for pin in 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.
|
# every instance must have a set of connections, even if it is empty.
|
||||||
if len(self.insts) != len(self.conns):
|
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.insts),
|
||||||
len(self.conns)))
|
len(self.conns)))
|
||||||
debug.error("Instances: \n" + str(self.insts))
|
debug.error("Instances: \n" + str(self.insts))
|
||||||
|
|
@ -330,9 +330,9 @@ class spice():
|
||||||
else:
|
else:
|
||||||
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
|
sp.write("X{0} {1} {2}\n".format(self.insts[i].name,
|
||||||
" ".join(self.conns[i]),
|
" ".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:
|
else:
|
||||||
# If spice is a hard module, output the spice file contents.
|
# If spice is a hard module, output the spice file contents.
|
||||||
|
|
@ -390,7 +390,7 @@ class spice():
|
||||||
.format(self.__class__.__name__))
|
.format(self.__class__.__name__))
|
||||||
debug.warning("Class {0} name {1}"
|
debug.warning("Class {0} name {1}"
|
||||||
.format(self.__class__.__name__,
|
.format(self.__class__.__name__,
|
||||||
self.name))
|
self.cell_name))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
|
|
@ -408,7 +408,7 @@ class spice():
|
||||||
.format(self.__class__.__name__))
|
.format(self.__class__.__name__))
|
||||||
debug.warning("Class {0} name {1}"
|
debug.warning("Class {0} name {1}"
|
||||||
.format(self.__class__.__name__,
|
.format(self.__class__.__name__,
|
||||||
self.name))
|
self.cell_name))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5):
|
def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5):
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,9 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import gdsMill
|
|
||||||
import tech
|
|
||||||
import globals
|
|
||||||
import math
|
|
||||||
import debug
|
import debug
|
||||||
import datetime
|
from tech import layer_names
|
||||||
from collections import defaultdict
|
|
||||||
import pdb
|
|
||||||
|
|
||||||
class lef:
|
class lef:
|
||||||
"""
|
"""
|
||||||
|
|
@ -20,13 +15,13 @@ class lef:
|
||||||
and write them to LEF file.
|
and write them to LEF file.
|
||||||
This is inherited by the sram_base class.
|
This is inherited by the sram_base class.
|
||||||
"""
|
"""
|
||||||
def __init__(self,layers):
|
def __init__(self, layers):
|
||||||
# LEF db units per micron
|
# LEF db units per micron
|
||||||
self.lef_units = 2000
|
self.lef_units = 2000
|
||||||
# These are the layers of the obstructions
|
# These are the layers of the obstructions
|
||||||
self.lef_layers = layers
|
self.lef_layers = layers
|
||||||
# Round to ensure float values are divisible by 0.0025 (the manufacturing grid)
|
# 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):
|
def lef_write(self, lef_name):
|
||||||
"""Write the entire lef of the object to the file."""
|
"""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.indent = "" # To maintain the indent level easily
|
||||||
|
|
||||||
self.lef = open(lef_name,"w")
|
self.lef = open(lef_name, "w")
|
||||||
self.lef_write_header()
|
self.lef_write_header()
|
||||||
for pin in self.pins:
|
for pin in self.pins:
|
||||||
self.lef_write_pin(pin)
|
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(" DATABASE MICRONS {0} ;\n".format(self.lef_units))
|
||||||
self.lef.write("END UNITS\n")
|
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.indent += " "
|
||||||
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
|
self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent))
|
||||||
self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent,
|
self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent,
|
||||||
round(self.width,self.round_grid),
|
round(self.width, self.round_grid),
|
||||||
round(self.height,self.round_grid)))
|
round(self.height, self.round_grid)))
|
||||||
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
|
self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent))
|
||||||
|
|
||||||
def lef_write_footer(self):
|
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.indent = self.indent[:-3]
|
||||||
self.lef.write("END LIBRARY\n")
|
self.lef.write("END LIBRARY\n")
|
||||||
|
|
||||||
|
|
||||||
def lef_write_pin(self, name):
|
def lef_write_pin(self, name):
|
||||||
pin_dir = self.get_pin_dir(name)
|
pin_dir = self.get_pin_dir(name)
|
||||||
pin_type = self.get_pin_type(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.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"]:
|
if pin_type in ["POWER", "GROUND"]:
|
||||||
self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type))
|
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}SHAPE ABUTMENT ; \n".format(self.indent))
|
||||||
|
|
||||||
self.lef.write("{0}PORT\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.
|
# We could sort these together to minimize different layer sections, but meh.
|
||||||
pin_list = self.get_pins(name)
|
pin_list = self.get_pins(name)
|
||||||
for pin in pin_list:
|
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)
|
self.lef_write_shape(pin.rect)
|
||||||
|
|
||||||
# End the PORT
|
# End the PORT
|
||||||
|
|
@ -93,19 +87,16 @@ class lef:
|
||||||
|
|
||||||
# End the PIN
|
# End the PIN
|
||||||
self.indent = self.indent[:-3]
|
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):
|
def lef_write_obstructions(self):
|
||||||
""" Write all the obstructions on each layer """
|
""" Write all the obstructions on each layer """
|
||||||
self.lef.write("{0}OBS\n".format(self.indent))
|
self.lef.write("{0}OBS\n".format(self.indent))
|
||||||
for layer in self.lef_layers:
|
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 += " "
|
self.indent += " "
|
||||||
# pdb.set_trace()
|
blockages = self.get_blockages(layer, True)
|
||||||
blockages = self.get_blockages(layer,True)
|
|
||||||
for b in blockages:
|
for b in blockages:
|
||||||
# if len(b) > 2:
|
|
||||||
# print(b)
|
|
||||||
self.lef_write_shape(b)
|
self.lef_write_shape(b)
|
||||||
self.indent = self.indent[:-3]
|
self.indent = self.indent[:-3]
|
||||||
self.lef.write("{0}END\n".format(self.indent))
|
self.lef.write("{0}END\n".format(self.indent))
|
||||||
|
|
@ -116,13 +107,19 @@ class lef:
|
||||||
self.lef.write("{0}RECT ".format(self.indent))
|
self.lef.write("{0}RECT ".format(self.indent))
|
||||||
for item in rect:
|
for item in rect:
|
||||||
# print(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")
|
self.lef.write(" ;\n")
|
||||||
else:
|
else:
|
||||||
""" Write a LEF polygon """
|
""" Write a LEF polygon """
|
||||||
self.lef.write("{0}POLYGON ".format(self.indent))
|
self.lef.write("{0}POLYGON ".format(self.indent))
|
||||||
for item in rect:
|
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)):
|
# 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(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid)))
|
||||||
self.lef.write(" ;\n")
|
self.lef.write(" ;\n")
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
|
||||||
|
import os
|
||||||
|
import math
|
||||||
|
|
||||||
import gdsMill
|
import gdsMill
|
||||||
import tech
|
import tech
|
||||||
import math
|
|
||||||
import globals
|
import globals
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
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.
|
Return these as a set of properties including the cell width/height too.
|
||||||
"""
|
"""
|
||||||
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
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 = {}
|
cell = {}
|
||||||
measure_result = cell_vlsi.getLayoutBorder(lpp[0])
|
measure_result = cell_vlsi.getLayoutBorder(lpp[0])
|
||||||
if measure_result:
|
if measure_result:
|
||||||
|
|
@ -73,22 +76,47 @@ def auto_measure_libcell(pin_list, name, units, lpp):
|
||||||
return cell
|
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):
|
def get_gds_size(name, gds_filename, units, lpp):
|
||||||
"""
|
"""
|
||||||
Open a GDS file and return the size from either the
|
Open a GDS file and return the size from either the
|
||||||
bounding box or a border layer.
|
bounding box or a border layer.
|
||||||
"""
|
"""
|
||||||
debug.info(4, "Creating VLSI layout for {}".format(name))
|
k = (name, os.path.realpath(gds_filename), units, lpp)
|
||||||
cell_vlsi = gdsMill.VlsiLayout(units=units)
|
try:
|
||||||
reader = gdsMill.Gds2reader(cell_vlsi)
|
return _GDS_SIZE_CACHE[k]
|
||||||
reader.loadFromFile(gds_filename)
|
except KeyError:
|
||||||
|
cell_vlsi = _get_gds_reader(units, gds_filename)
|
||||||
|
|
||||||
measure_result = cell_vlsi.getLayoutBorder(lpp)
|
measure_result = cell_vlsi.getLayoutBorder(lpp)
|
||||||
if not measure_result:
|
if not measure_result:
|
||||||
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
|
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
|
||||||
measure_result = cell_vlsi.measureSize(name)
|
measure_result = cell_vlsi.measureSize(name)
|
||||||
# returns width,height
|
|
||||||
return measure_result
|
_GDS_SIZE_CACHE[k] = measure_result
|
||||||
|
|
||||||
|
# returns width,height
|
||||||
|
return measure_result
|
||||||
|
|
||||||
|
|
||||||
def get_libcell_size(name, units, lpp):
|
def get_libcell_size(name, units, lpp):
|
||||||
|
|
@ -101,27 +129,34 @@ def get_libcell_size(name, units, lpp):
|
||||||
return(get_gds_size(name, cell_gds, units, lpp))
|
return(get_gds_size(name, cell_gds, units, lpp))
|
||||||
|
|
||||||
|
|
||||||
|
_GDS_PINS_CACHE = {}
|
||||||
|
|
||||||
|
|
||||||
def get_gds_pins(pin_names, name, gds_filename, units):
|
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.
|
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.
|
Return these as a rectangle layer pair for each pin.
|
||||||
"""
|
"""
|
||||||
cell_vlsi = gdsMill.VlsiLayout(units=units)
|
k = (tuple(pin_names), name, os.path.realpath(gds_filename), units)
|
||||||
reader = gdsMill.Gds2reader(cell_vlsi)
|
try:
|
||||||
reader.loadFromFile(gds_filename)
|
return dict(_GDS_PINS_CACHE[k])
|
||||||
|
except KeyError:
|
||||||
|
cell_vlsi = _get_gds_reader(units, gds_filename)
|
||||||
|
|
||||||
cell = {}
|
cell = {}
|
||||||
for pin_name in pin_names:
|
for pin_name in pin_names:
|
||||||
cell[str(pin_name)] = []
|
cell[str(pin_name)] = []
|
||||||
pin_list = cell_vlsi.getPinShape(str(pin_name))
|
pin_list = cell_vlsi.getPinShape(str(pin_name))
|
||||||
for pin_shape in pin_list:
|
for pin_shape in pin_list:
|
||||||
(lpp, boundary) = pin_shape
|
(lpp, boundary) = pin_shape
|
||||||
rect = [vector(boundary[0], boundary[1]),
|
rect = [vector(boundary[0], boundary[1]),
|
||||||
vector(boundary[2], boundary[3])]
|
vector(boundary[2], boundary[3])]
|
||||||
# this is a list because other cells/designs
|
# this is a list because other cells/designs
|
||||||
# may have must-connect pins
|
# may have must-connect pins
|
||||||
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
|
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):
|
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"
|
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
||||||
return(get_gds_pins(pin_list, name, cell_gds, units))
|
return(get_gds_pins(pin_list, name, cell_gds, units))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,9 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
from globals import OPTS
|
|
||||||
|
|
||||||
class bitcell(bitcell_base.bitcell_base):
|
class bitcell(bitcell_base.bitcell_base):
|
||||||
"""
|
"""
|
||||||
|
|
@ -20,8 +18,6 @@ class bitcell(bitcell_base.bitcell_base):
|
||||||
library.
|
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,
|
pin_names = [props.bitcell.cell_6t.pin.bl,
|
||||||
props.bitcell.cell_6t.pin.br,
|
props.bitcell.cell_6t.pin.br,
|
||||||
props.bitcell.cell_6t.pin.wl,
|
props.bitcell.cell_6t.pin.wl,
|
||||||
|
|
@ -30,24 +26,12 @@ class bitcell(bitcell_base.bitcell_base):
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
storage_nets = ['Q', 'Q_bar']
|
storage_nets = ['Q', 'Q_bar']
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("cell_6t",
|
def __init__(self, name):
|
||||||
GDS["unit"],
|
super().__init__(name)
|
||||||
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")
|
|
||||||
debug.info(2, "Create bitcell")
|
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)
|
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):
|
def get_all_wl_names(self):
|
||||||
""" Creates a list of all wordline pin names """
|
""" Creates a list of all wordline pin names """
|
||||||
row_pins = [props.bitcell.cell_6t.pin.wl]
|
row_pins = [props.bitcell.cell_6t.pin.wl]
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer, parameter, drc
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import logical_effort
|
|
||||||
import bitcell_base
|
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.wl1,
|
||||||
props.bitcell.cell_1rw1r.pin.vdd,
|
props.bitcell.cell_1rw1r.pin.vdd,
|
||||||
props.bitcell.cell_1rw1r.pin.gnd]
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
||||||
"INPUT", "INPUT", "POWER", "GROUND"]
|
"INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
storage_nets = ['Q', 'Q_bar']
|
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=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r")
|
|
||||||
debug.info(2, "Create bitcell with 1RW and 1R Port")
|
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)
|
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.bl_names = [pin_names[0], pin_names[2]]
|
||||||
self.br_names = [pin_names[1], pin_names[3]]
|
self.br_names = [pin_names[1], pin_names[3]]
|
||||||
self.wl_names = [pin_names[4], pin_names[5]]
|
self.wl_names = [pin_names[4], pin_names[5]]
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
@ -31,28 +29,18 @@ class bitcell_1w_1r(bitcell_base.bitcell_base):
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
|
||||||
"INPUT", "INPUT", "POWER", "GROUND"]
|
"INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
storage_nets = ['Q', 'Q_bar']
|
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=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "cell_1w_1r")
|
|
||||||
debug.info(2, "Create bitcell with 1W and 1R Port")
|
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)
|
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.bl_names = [pin_names[0], pin_names[2]]
|
||||||
self.br_names = [pin_names[1], pin_names[3]]
|
self.br_names = [pin_names[1], pin_names[3]]
|
||||||
self.wl_names = [pin_names[4], pin_names[5]]
|
self.wl_names = [pin_names[4], pin_names[5]]
|
||||||
|
|
||||||
|
|
||||||
def get_bitcell_pins(self, col, row):
|
def get_bitcell_pins(self, col, row):
|
||||||
"""
|
"""
|
||||||
Creates a list of connections in the bitcell,
|
Creates a list of connections in the bitcell,
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,30 @@
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
|
import utils
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import logical_effort
|
import logical_effort
|
||||||
from tech import parameter, drc, layer
|
from tech import GDS, parameter, drc, layer
|
||||||
|
|
||||||
|
|
||||||
class bitcell_base(design.design):
|
class bitcell_base(design.design):
|
||||||
"""
|
"""
|
||||||
Base bitcell parameters to be over-riden.
|
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)
|
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):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
# This accounts for bitline being drained
|
# This accounts for bitline being drained
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,25 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
||||||
class col_cap_bitcell_1rw_1r(bitcell_base.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,
|
pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
|
||||||
props.bitcell.cell_1rw1r.pin.br0,
|
props.bitcell.cell_1rw1r.pin.br0,
|
||||||
props.bitcell.cell_1rw1r.pin.bl1,
|
props.bitcell.cell_1rw1r.pin.bl1,
|
||||||
props.bitcell.cell_1rw1r.pin.br1,
|
props.bitcell.cell_1rw1r.pin.br1,
|
||||||
props.bitcell.cell_1rw1r.pin.vdd]
|
props.bitcell.cell_1rw1r.pin.vdd]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
||||||
"POWER", "GROUND"]
|
"POWER", "GROUND"]
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r",
|
def __init__(self, name="col_cap_cell_1rw_1r"):
|
||||||
GDS["unit"],
|
bitcell_base.bitcell_base.__init__(self, name)
|
||||||
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")
|
|
||||||
debug.info(2, "Create col_cap bitcell 1rw+1r object")
|
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
|
self.no_instances = True
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
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.wl,
|
||||||
props.bitcell.cell_6t.pin.vdd,
|
props.bitcell.cell_6t.pin.vdd,
|
||||||
props.bitcell.cell_6t.pin.gnd]
|
props.bitcell.cell_6t.pin.gnd]
|
||||||
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("dummy_cell_6t",
|
def __init__(self, name):
|
||||||
GDS["unit"],
|
super().__init__(name)
|
||||||
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")
|
|
||||||
debug.info(2, "Create dummy bitcell")
|
debug.info(2, "Create dummy bitcell")
|
||||||
|
|
||||||
self.width = dummy_bitcell.width
|
|
||||||
self.height = dummy_bitcell.height
|
|
||||||
self.pin_map = dummy_bitcell.pin_map
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
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.wl1,
|
||||||
props.bitcell.cell_1rw1r.pin.vdd,
|
props.bitcell.cell_1rw1r.pin.vdd,
|
||||||
props.bitcell.cell_1rw1r.pin.gnd]
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
||||||
"INPUT", "INPUT", "POWER", "GROUND"]
|
"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=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
|
|
||||||
debug.info(2, "Create dummy bitcell 1rw+1r object")
|
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)
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
@ -29,21 +27,11 @@ class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
|
||||||
props.bitcell.cell_1w1r.pin.gnd]
|
props.bitcell.cell_1w1r.pin.gnd]
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
|
||||||
"INPUT", "INPUT", "POWER", "GROUND"]
|
"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=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
|
|
||||||
debug.info(2, "Create dummy bitcell 1w+1r object")
|
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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,17 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import drc, spice,parameter
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class dummy_pbitcell(design.design):
|
class dummy_pbitcell(design.design):
|
||||||
"""
|
"""
|
||||||
Creates a replica bitcell using pbitcell
|
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_rw_ports = OPTS.num_rw_ports
|
||||||
self.num_w_ports = OPTS.num_w_ports
|
self.num_w_ports = OPTS.num_w_ports
|
||||||
self.num_r_ports = OPTS.num_r_ports
|
self.num_r_ports = OPTS.num_r_ports
|
||||||
|
|
@ -54,7 +54,8 @@ class dummy_pbitcell(design.design):
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def add_modules(self):
|
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.add_mod(self.prbc)
|
||||||
|
|
||||||
self.height = self.prbc.height
|
self.height = self.prbc.height
|
||||||
|
|
@ -75,7 +76,7 @@ class dummy_pbitcell(design.design):
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def place_pbitcell(self):
|
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):
|
def route_rbc_connections(self):
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.replica_bitcell = replica_bitcell
|
self.replica_bitcell = replica_bitcell
|
||||||
self.dummy_bitcell = dummy_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"
|
fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
|
||||||
info_string = fmt_str.format(self.num_rw_ports,
|
info_string = fmt_str.format(self.num_rw_ports,
|
||||||
self.num_w_ports,
|
self.num_w_ports,
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,14 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
|
||||||
import debug
|
import debug
|
||||||
import utils
|
import bitcell_base
|
||||||
from tech import GDS,layer,drc,parameter,cell_properties
|
|
||||||
from tech import cell_properties as props
|
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.)
|
A single bit cell (6T, 8T, etc.)
|
||||||
This module implements the single memory cell used in the design. It
|
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]
|
props.bitcell.cell_6t.pin.gnd]
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
if not OPTS.netlist_only:
|
def __init__(self, name):
|
||||||
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
|
super().__init__(name)
|
||||||
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")
|
|
||||||
debug.info(2, "Create replica bitcell object")
|
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):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
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.
|
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
|
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)
|
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""Return the relative capacitance of the access transistor gates"""
|
||||||
|
|
||||||
# FIXME: This applies to bitline capacitances as well.
|
# FIXME: This applies to bitline capacitances as well.
|
||||||
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2 * access_tx_cin
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Bitcell power in nW. Only characterizes leakage."""
|
"""Bitcell power in nW. Only characterizes leakage."""
|
||||||
from tech import spice
|
from tech import spice
|
||||||
leakage = spice["bitcell_leakage"]
|
leakage = spice["bitcell_leakage"]
|
||||||
dynamic = 0 #temporary
|
dynamic = 0 # FIXME
|
||||||
total_power = self.return_power(dynamic, leakage)
|
total_power = self.return_power(dynamic, leakage)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
|
||||||
import debug
|
import debug
|
||||||
import utils
|
import bitcell_base
|
||||||
from tech import GDS,layer,drc,parameter
|
|
||||||
from tech import cell_properties as props
|
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.
|
A single bit cell which is forced to store a 0.
|
||||||
This module implements the single memory cell used in the design. It
|
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.wl1,
|
||||||
props.bitcell.cell_1rw1r.pin.vdd,
|
props.bitcell.cell_1rw1r.pin.vdd,
|
||||||
props.bitcell.cell_1rw1r.pin.gnd]
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
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=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
design.design.__init__(self, "replica_cell_1rw_1r")
|
|
||||||
debug.info(2, "Create replica bitcell 1rw+1r object")
|
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):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
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.
|
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
|
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)
|
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""Return the relative capacitance of the access transistor gates"""
|
||||||
|
|
||||||
# FIXME: This applies to bitline capacitances as well.
|
# FIXME: This applies to bitline capacitances as well.
|
||||||
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
# 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"]
|
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2 * access_tx_cin
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""Adds edges to graph. Multiport bitcell timing graph is too complex
|
"""Adds edges to graph. Multiport bitcell timing graph is too complex
|
||||||
to use the add_graph_edges function."""
|
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
|
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
|
# 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.bl0], self)
|
||||||
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self)
|
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self)
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
|
||||||
import debug
|
import debug
|
||||||
import utils
|
import bitcell_base
|
||||||
from tech import GDS,layer,drc,parameter
|
|
||||||
from tech import cell_properties as props
|
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.
|
A single bit cell which is forced to store a 0.
|
||||||
This module implements the single memory cell used in the design. It
|
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.wl1,
|
||||||
props.bitcell.cell_1w1r.pin.vdd,
|
props.bitcell.cell_1w1r.pin.vdd,
|
||||||
props.bitcell.cell_1w1r.pin.gnd]
|
props.bitcell.cell_1w1r.pin.gnd]
|
||||||
|
|
||||||
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
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=""):
|
def __init__(self, name):
|
||||||
# Ignore the name argument
|
super().__init__(name)
|
||||||
design.design.__init__(self, "replica_cell_1w_1r")
|
|
||||||
debug.info(2, "Create replica bitcell 1w+1r object")
|
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):
|
def get_stage_effort(self, load):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
|
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.
|
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
|
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)
|
return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""Return the relative capacitance of the access transistor gates"""
|
||||||
|
|
||||||
# FIXME: This applies to bitline capacitances as well.
|
# FIXME: This applies to bitline capacitances as well.
|
||||||
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
# 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"]
|
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2 * access_tx_cin
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""Adds edges to graph. Multiport bitcell timing graph is too complex
|
"""Adds edges to graph. Multiport bitcell timing graph is too complex
|
||||||
to use the add_graph_edges function."""
|
to use the add_graph_edges function."""
|
||||||
debug.info(1,'Adding edges for {}'.format(inst_name))
|
debug.info(1, 'Adding edges for {}'.format(inst_name))
|
||||||
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_1w1r.pin
|
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
|
# 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.bl1], self)
|
||||||
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)
|
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,25 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import drc, spice,parameter
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class replica_pbitcell(design.design):
|
class replica_pbitcell(design.design):
|
||||||
"""
|
"""
|
||||||
Creates a replica bitcell using pbitcell
|
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_rw_ports = OPTS.num_rw_ports
|
||||||
self.num_w_ports = OPTS.num_w_ports
|
self.num_w_ports = OPTS.num_w_ports
|
||||||
self.num_r_ports = OPTS.num_r_ports
|
self.num_r_ports = OPTS.num_r_ports
|
||||||
self.total_ports = self.num_rw_ports + self.num_w_ports + self.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,
|
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_w_ports,
|
||||||
self.num_r_ports))
|
self.num_r_ports))
|
||||||
|
|
@ -54,7 +56,8 @@ class replica_pbitcell(design.design):
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def add_modules(self):
|
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.add_mod(self.prbc)
|
||||||
|
|
||||||
self.height = self.prbc.height
|
self.height = self.prbc.height
|
||||||
|
|
@ -75,7 +78,7 @@ class replica_pbitcell(design.design):
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
def place_pbitcell(self):
|
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):
|
def route_rbc_connections(self):
|
||||||
for port in range(self.total_ports):
|
for port in range(self.total_ports):
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,22 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
|
||||||
from tech import GDS, layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
||||||
class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
"""
|
"""
|
||||||
A single bit cell which is forced to store a 0.
|
Row end cap cell.
|
||||||
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. """
|
|
||||||
|
|
||||||
pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
|
pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
|
||||||
props.bitcell.cell_1rw1r.pin.wl1,
|
props.bitcell.cell_1rw1r.pin.wl1,
|
||||||
props.bitcell.cell_1rw1r.pin.gnd]
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
|
||||||
type_list = ["INPUT", "INPUT", "GROUND"]
|
type_list = ["INPUT", "INPUT", "GROUND"]
|
||||||
|
|
||||||
(width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r",
|
def __init__(self, name="row_cap_cell_1rw_1r"):
|
||||||
GDS["unit"],
|
bitcell_base.bitcell_base.__init__(self, name)
|
||||||
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")
|
|
||||||
debug.info(2, "Create row_cap bitcell 1rw+1r object")
|
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
|
self.no_instances = True
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,8 @@ class delay(simulation):
|
||||||
self.create_test_cycles()
|
self.create_test_cycles()
|
||||||
|
|
||||||
# creates and opens stimulus file for writing
|
# 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 = 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.sf.write("* Delay stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(self.period,
|
||||||
self.load,
|
self.load,
|
||||||
|
|
@ -350,7 +351,8 @@ class delay(simulation):
|
||||||
self.check_arguments()
|
self.check_arguments()
|
||||||
|
|
||||||
# creates and opens stimulus file for writing
|
# 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 = open(temp_stim, "w")
|
||||||
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
|
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
|
||||||
self.stim = stimuli(self.sf, self.corner)
|
self.stim = stimuli(self.sf, self.corner)
|
||||||
|
|
@ -616,7 +618,7 @@ class delay(simulation):
|
||||||
|
|
||||||
self.write_delay_stimulus()
|
self.write_delay_stimulus()
|
||||||
|
|
||||||
self.stim.run_sim()
|
self.stim.run_sim(self.delay_stim_sp)
|
||||||
|
|
||||||
return self.check_measurements()
|
return self.check_measurements()
|
||||||
|
|
||||||
|
|
@ -772,7 +774,7 @@ class delay(simulation):
|
||||||
|
|
||||||
debug.info(1, "Performing leakage power simulations.")
|
debug.info(1, "Performing leakage power simulations.")
|
||||||
self.write_power_stimulus(trim=False)
|
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")
|
leakage_power=parse_spice_list("timing", "leakage_power")
|
||||||
debug.check(leakage_power!="Failed", "Could not measure 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))
|
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)
|
# sys.exit(1)
|
||||||
|
|
||||||
self.write_power_stimulus(trim=True)
|
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")
|
trim_leakage_power=parse_spice_list("timing", "leakage_power")
|
||||||
debug.check(trim_leakage_power!="Failed", "Could not measure 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))
|
debug.info(1, "Leakage power of trimmed array is {0} mW".format(trim_leakage_power * 1e3))
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,24 @@ class functional(simulation):
|
||||||
for successful SRAM operation.
|
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)
|
super().__init__(sram, spfile, corner)
|
||||||
|
|
||||||
# Seed the characterizer with a constant seed for unit tests
|
# Seed the characterizer with a constant seed for unit tests
|
||||||
if OPTS.is_unit_test:
|
if OPTS.is_unit_test:
|
||||||
random.seed(12345)
|
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:
|
if self.write_size:
|
||||||
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
|
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
|
||||||
else:
|
else:
|
||||||
|
|
@ -58,15 +69,14 @@ class functional(simulation):
|
||||||
self.read_check = []
|
self.read_check = []
|
||||||
self.read_results = []
|
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
|
# Generate a random sequence of reads and writes
|
||||||
self.create_random_memory_sequence()
|
self.create_random_memory_sequence()
|
||||||
|
|
||||||
# Run SPICE simulation
|
# Write SPICE simulation
|
||||||
self.write_functional_stimulus()
|
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.
|
# 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()
|
(success, error) = self.read_stim_results()
|
||||||
|
|
@ -330,7 +340,8 @@ class functional(simulation):
|
||||||
|
|
||||||
def write_functional_stimulus(self):
|
def write_functional_stimulus(self):
|
||||||
""" Writes SPICE stimulus. """
|
""" 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 = open(temp_stim, "w")
|
||||||
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
|
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
|
||||||
self.stim = stimuli(self.sf, self.corner)
|
self.stim = stimuli(self.sf, self.corner)
|
||||||
|
|
|
||||||
|
|
@ -27,23 +27,22 @@ class setup_hold():
|
||||||
self.model_location = OPTS.openram_tech + "sp_lib/dff.sp"
|
self.model_location = OPTS.openram_tech + "sp_lib/dff.sp"
|
||||||
self.period = tech.spice["feasible_period"]
|
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)
|
self.set_corner(corner)
|
||||||
|
|
||||||
|
def set_corner(self, corner):
|
||||||
def set_corner(self,corner):
|
|
||||||
""" Set the corner values """
|
""" Set the corner values """
|
||||||
self.corner = corner
|
self.corner = corner
|
||||||
(self.process, self.vdd_voltage, self.temperature) = corner
|
(self.process, self.vdd_voltage, self.temperature) = corner
|
||||||
self.gnd_voltage = 0
|
self.gnd_voltage = 0
|
||||||
|
|
||||||
|
|
||||||
def write_stimulus(self, mode, target_time, correct_value):
|
def write_stimulus(self, mode, target_time, correct_value):
|
||||||
"""Creates a stimulus file for SRAM setup/hold time calculation"""
|
"""Creates a stimulus file for SRAM setup/hold time calculation"""
|
||||||
|
|
||||||
# creates and opens the stimulus file for writing
|
# 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.sf = open(temp_stim, "w")
|
||||||
self.stim = stimuli(self.sf, self.corner)
|
self.stim = stimuli(self.sf, self.corner)
|
||||||
|
|
||||||
|
|
@ -63,8 +62,7 @@ class setup_hold():
|
||||||
self.write_measures(mode=mode,
|
self.write_measures(mode=mode,
|
||||||
correct_value=correct_value)
|
correct_value=correct_value)
|
||||||
|
|
||||||
|
self.stim.write_control(4 * self.period)
|
||||||
self.stim.write_control(4*self.period)
|
|
||||||
|
|
||||||
self.sf.close()
|
self.sf.close()
|
||||||
|
|
||||||
|
|
@ -79,7 +77,6 @@ class setup_hold():
|
||||||
self.sf.write("\n* Global Power Supplies\n")
|
self.sf.write("\n* Global Power Supplies\n")
|
||||||
self.stim.write_supply()
|
self.stim.write_supply()
|
||||||
|
|
||||||
|
|
||||||
def write_data(self, mode, target_time, correct_value):
|
def write_data(self, mode, target_time, correct_value):
|
||||||
"""Create the data signals for setup/hold analysis. First period is to
|
"""Create the data signals for setup/hold analysis. First period is to
|
||||||
initialize it to the opposite polarity. Second period is used for
|
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.
|
# without using .IC on an internal node.
|
||||||
# Return input to value after one period.
|
# Return input to value after one period.
|
||||||
# The second pulse is the characterization one at 2*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],
|
data_values=[0, 1, 0, 1],
|
||||||
period=2*self.period,
|
period=2 * self.period,
|
||||||
slew=self.constrained_input_slew,
|
slew=self.constrained_input_slew,
|
||||||
setup=0)
|
setup=0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def write_measures(self, mode, correct_value):
|
def write_measures(self, mode, correct_value):
|
||||||
""" Measure statements for setup/hold with right phases. """
|
""" Measure statements for setup/hold with right phases. """
|
||||||
|
|
||||||
|
|
@ -139,7 +134,6 @@ class setup_hold():
|
||||||
else:
|
else:
|
||||||
din_rise_or_fall = "RISE"
|
din_rise_or_fall = "RISE"
|
||||||
|
|
||||||
|
|
||||||
self.sf.write("\n* Measure statements for pass/fail verification\n")
|
self.sf.write("\n* Measure statements for pass/fail verification\n")
|
||||||
trig_name = "clk"
|
trig_name = "clk"
|
||||||
targ_name = "dout"
|
targ_name = "dout"
|
||||||
|
|
@ -152,8 +146,8 @@ class setup_hold():
|
||||||
targ_val=targ_val,
|
targ_val=targ_val,
|
||||||
trig_dir="RISE",
|
trig_dir="RISE",
|
||||||
targ_dir=dout_rise_or_fall,
|
targ_dir=dout_rise_or_fall,
|
||||||
trig_td=1.9*self.period,
|
trig_td=1.9 * self.period,
|
||||||
targ_td=1.9*self.period)
|
targ_td=1.9 * self.period)
|
||||||
|
|
||||||
targ_name = "data"
|
targ_name = "data"
|
||||||
# Start triggers right after initialize value is returned to normal
|
# Start triggers right after initialize value is returned to normal
|
||||||
|
|
@ -165,11 +159,8 @@ class setup_hold():
|
||||||
targ_val=targ_val,
|
targ_val=targ_val,
|
||||||
trig_dir="RISE",
|
trig_dir="RISE",
|
||||||
targ_dir=din_rise_or_fall,
|
targ_dir=din_rise_or_fall,
|
||||||
trig_td=1.2*self.period,
|
trig_td=1.2 * self.period,
|
||||||
targ_td=1.2*self.period)
|
targ_td=1.2 * self.period)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def bidir_search(self, correct_value, mode):
|
def bidir_search(self, correct_value, mode):
|
||||||
""" This will perform a bidirectional search for either setup or hold times.
|
""" 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
|
# this time. They are also unbalanced so that the average won't be right on the clock edge in the
|
||||||
# first iteration.
|
# first iteration.
|
||||||
if mode == "SETUP":
|
if mode == "SETUP":
|
||||||
feasible_bound = 1.25*self.period
|
feasible_bound = 1.25 * self.period
|
||||||
infeasible_bound = 2.5*self.period
|
infeasible_bound = 2.5 * self.period
|
||||||
else:
|
else:
|
||||||
infeasible_bound = 1.5*self.period
|
infeasible_bound = 1.5 * self.period
|
||||||
feasible_bound = 2.75*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!
|
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
|
||||||
self.write_stimulus(mode=mode,
|
self.write_stimulus(mode=mode,
|
||||||
target_time=feasible_bound,
|
target_time=feasible_bound,
|
||||||
correct_value=correct_value)
|
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"))
|
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"))
|
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))
|
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:
|
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
|
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||||
setuphold_time *= -1e9
|
setuphold_time *= -1e9
|
||||||
|
|
@ -206,57 +200,53 @@ class setup_hold():
|
||||||
setuphold_time *= 1e9
|
setuphold_time *= 1e9
|
||||||
|
|
||||||
passing_setuphold_time = setuphold_time
|
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,
|
setuphold_time,
|
||||||
feasible_bound,
|
feasible_bound,
|
||||||
2*self.period))
|
2 * self.period))
|
||||||
#raw_input("Press Enter to continue...")
|
#raw_input("Press Enter to continue...")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
target_time = (feasible_bound + infeasible_bound)/2
|
target_time = (feasible_bound + infeasible_bound) / 2
|
||||||
self.write_stimulus(mode=mode,
|
self.write_stimulus(mode=mode,
|
||||||
target_time=target_time,
|
target_time=target_time,
|
||||||
correct_value=correct_value)
|
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,
|
correct_value,
|
||||||
target_time,
|
target_time,
|
||||||
infeasible_bound,
|
infeasible_bound,
|
||||||
feasible_bound))
|
feasible_bound))
|
||||||
|
|
||||||
|
self.stim.run_sim(self.stim_sp)
|
||||||
self.stim.run_sim()
|
|
||||||
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||||
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
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
|
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||||
setuphold_time *= -1e9
|
setuphold_time *= -1e9
|
||||||
else:
|
else:
|
||||||
setuphold_time *= 1e9
|
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
|
passing_setuphold_time = setuphold_time
|
||||||
feasible_bound = target_time
|
feasible_bound = target_time
|
||||||
else:
|
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
|
infeasible_bound = target_time
|
||||||
|
|
||||||
#raw_input("Press Enter to continue...")
|
|
||||||
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
|
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
|
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
|
return passing_setuphold_time
|
||||||
|
|
||||||
|
|
||||||
def setup_LH_time(self):
|
def setup_LH_time(self):
|
||||||
"""Calculates the setup time for low-to-high transition for a DFF
|
"""Calculates the setup time for low-to-high transition for a DFF
|
||||||
"""
|
"""
|
||||||
return self.bidir_search(1, "SETUP")
|
return self.bidir_search(1, "SETUP")
|
||||||
|
|
||||||
|
|
||||||
def setup_HL_time(self):
|
def setup_HL_time(self):
|
||||||
"""Calculates the setup time for high-to-low transition for a DFF
|
"""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")
|
return self.bidir_search(0, "HOLD")
|
||||||
|
|
||||||
|
|
||||||
def analyze(self, related_slews, constrained_slews):
|
def analyze(self, related_slews, constrained_slews):
|
||||||
"""main function to calculate both setup and hold time for the
|
"""main function to calculate both setup and hold time for the
|
||||||
DFF and returns a dictionary that contains 4 lists for both
|
DFF and returns a dictionary that contains 4 lists for both
|
||||||
|
|
@ -301,10 +290,10 @@ class setup_hold():
|
||||||
# }
|
# }
|
||||||
# return times
|
# return times
|
||||||
|
|
||||||
|
|
||||||
for self.related_input_slew in related_slews:
|
for self.related_input_slew in related_slews:
|
||||||
for self.constrained_input_slew in constrained_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()
|
LH_setup_time = self.setup_LH_time()
|
||||||
debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time))
|
debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time))
|
||||||
HL_setup_time = self.setup_HL_time()
|
HL_setup_time = self.setup_HL_time()
|
||||||
|
|
@ -325,7 +314,7 @@ class setup_hold():
|
||||||
}
|
}
|
||||||
return times
|
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.
|
""" Just return the fixed setup/hold times from the technology.
|
||||||
"""
|
"""
|
||||||
LH_setup = []
|
LH_setup = []
|
||||||
|
|
@ -336,10 +325,10 @@ class setup_hold():
|
||||||
for self.related_input_slew in related_slews:
|
for self.related_input_slew in related_slews:
|
||||||
for self.constrained_input_slew in constrained_slews:
|
for self.constrained_input_slew in constrained_slews:
|
||||||
# convert from ps to ns
|
# convert from ps to ns
|
||||||
LH_setup.append(tech.spice["dff_setup"]/1e3)
|
LH_setup.append(tech.spice["dff_setup"] / 1e3)
|
||||||
HL_setup.append(tech.spice["dff_setup"]/1e3)
|
HL_setup.append(tech.spice["dff_setup"] / 1e3)
|
||||||
LH_hold.append(tech.spice["dff_hold"]/1e3)
|
LH_hold.append(tech.spice["dff_hold"] / 1e3)
|
||||||
HL_hold.append(tech.spice["dff_hold"]/1e3)
|
HL_hold.append(tech.spice["dff_hold"] / 1e3)
|
||||||
|
|
||||||
times = {"setup_times_LH": LH_setup,
|
times = {"setup_times_LH": LH_setup,
|
||||||
"setup_times_HL": HL_setup,
|
"setup_times_HL": HL_setup,
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,21 @@ class stimuli():
|
||||||
self.sf = stim_file
|
self.sf = stim_file
|
||||||
|
|
||||||
(self.process, self.voltage, self.temperature) = corner
|
(self.process, self.voltage, self.temperature) = corner
|
||||||
|
found = False
|
||||||
|
self.device_libraries = []
|
||||||
|
self.device_models = []
|
||||||
try:
|
try:
|
||||||
self.device_libraries = tech.spice["fet_libraries"][self.process]
|
self.device_libraries += tech.spice["fet_libraries"][self.process]
|
||||||
except:
|
found = True
|
||||||
self.device_models = tech.spice["fet_models"][self.process]
|
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):
|
def inst_model(self, pins, model_name):
|
||||||
""" Function to instantiate a generic model with a set of pins """
|
""" Function to instantiate a generic model with a set of pins """
|
||||||
|
|
@ -249,7 +260,7 @@ class stimuli():
|
||||||
|
|
||||||
# create plots for all signals
|
# create plots for all signals
|
||||||
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
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"]:
|
if OPTS.spice_name in ["hspice", "xa"]:
|
||||||
self.sf.write(".probe V(*)\n")
|
self.sf.write(".probe V(*)\n")
|
||||||
else:
|
else:
|
||||||
|
|
@ -265,21 +276,16 @@ class stimuli():
|
||||||
"""Writes include statements, inputs are lists of model files"""
|
"""Writes include statements, inputs are lists of model files"""
|
||||||
|
|
||||||
self.sf.write("* {} process corner\n".format(self.process))
|
self.sf.write("* {} process corner\n".format(self.process))
|
||||||
if OPTS.tech_name == "sky130":
|
for item in self.device_libraries:
|
||||||
for item in self.device_libraries:
|
if os.path.isfile(item[0]):
|
||||||
if os.path.isfile(item[0]):
|
self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1]))
|
||||||
self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1]))
|
else:
|
||||||
else:
|
debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
|
||||||
debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0]))
|
|
||||||
includes = [circuit]
|
includes = self.device_models + [circuit]
|
||||||
else:
|
|
||||||
includes = self.device_models + [circuit]
|
|
||||||
|
|
||||||
for item in list(includes):
|
for item in list(includes):
|
||||||
if os.path.isfile(item):
|
self.sf.write(".include \"{0}\"\n".format(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):
|
def write_supply(self):
|
||||||
""" Writes supply voltage statements """
|
""" 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("\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))
|
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. """
|
""" 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
|
import datetime
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
|
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
import design
|
||||||
from tech import GDS, layer, spice, parameter
|
from tech import GDS, layer, spice
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
|
@ -23,18 +23,22 @@ class dff(design.design):
|
||||||
pin_names = props.dff.custom_port_list
|
pin_names = props.dff.custom_port_list
|
||||||
type_list = props.dff.custom_type_list
|
type_list = props.dff.custom_type_list
|
||||||
clk_pin = props.dff.clk_pin
|
clk_pin = props.dff.clk_pin
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("dff",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="dff"):
|
def __init__(self, name="dff"):
|
||||||
design.design.__init__(self, name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.width = dff.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = dff.height
|
GDS["unit"],
|
||||||
self.pin_map = dff.pin_map
|
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)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
|
|
@ -49,7 +53,6 @@ class dff(design.design):
|
||||||
|
|
||||||
def calculate_effective_capacitance(self, load):
|
def calculate_effective_capacitance(self, load):
|
||||||
"""Computes effective capacitance. Results in fF"""
|
"""Computes effective capacitance. Results in fF"""
|
||||||
from tech import parameter
|
|
||||||
c_load = load
|
c_load = load
|
||||||
c_para = spice["dff_out_cap"]#ff
|
c_para = spice["dff_out_cap"]#ff
|
||||||
transition_prob = 0.5
|
transition_prob = 0.5
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import design
|
||||||
from tech import GDS, layer, spice, parameter
|
from tech import GDS, layer, spice, parameter
|
||||||
import logical_effort
|
import logical_effort
|
||||||
import utils
|
import utils
|
||||||
import debug
|
|
||||||
|
|
||||||
|
|
||||||
class inv_dec(design.design):
|
class inv_dec(design.design):
|
||||||
|
|
@ -19,18 +18,22 @@ class inv_dec(design.design):
|
||||||
|
|
||||||
pin_names = ["A", "Z", "vdd", "gnd"]
|
pin_names = ["A", "Z", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("inv_dec",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="inv_dec", height=None):
|
def __init__(self, name="inv_dec", height=None):
|
||||||
design.design.__init__(self, name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.width = inv_dec.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = inv_dec.height
|
GDS["unit"],
|
||||||
self.pin_map = inv_dec.pin_map
|
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)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,22 @@ class nand2_dec(design.design):
|
||||||
|
|
||||||
pin_names = ["A", "B", "Z", "vdd", "gnd"]
|
pin_names = ["A", "B", "Z", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("nand2_dec",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="nand2_dec", height=None):
|
def __init__(self, name="nand2_dec", height=None):
|
||||||
design.design.__init__(self, name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.width = nand2_dec.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = nand2_dec.height
|
GDS["unit"],
|
||||||
self.pin_map = nand2_dec.pin_map
|
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)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
# FIXME: For now...
|
# FIXME: For now...
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,22 @@ class nand3_dec(design.design):
|
||||||
|
|
||||||
pin_names = ["A", "B", "C", "Z", "vdd", "gnd"]
|
pin_names = ["A", "B", "C", "Z", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("nand3_dec",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="nand3_dec", height=None):
|
def __init__(self, name="nand3_dec", height=None):
|
||||||
design.design.__init__(self, name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.width = nand3_dec.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = nand3_dec.height
|
GDS["unit"],
|
||||||
self.pin_map = nand3_dec.pin_map
|
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)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
# FIXME: For now...
|
# FIXME: For now...
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,22 @@ class nand4_dec(design.design):
|
||||||
|
|
||||||
pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
|
pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("nand4_dec",
|
|
||||||
GDS["unit"],
|
|
||||||
layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"])
|
|
||||||
|
|
||||||
def __init__(self, name="nand4_dec", height=None):
|
def __init__(self, name="nand4_dec", height=None):
|
||||||
design.design.__init__(self, name)
|
super().__init__(name)
|
||||||
|
|
||||||
self.width = nand4_dec.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = nand4_dec.height
|
GDS["unit"],
|
||||||
self.pin_map = nand4_dec.pin_map
|
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)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
# FIXME: For now...
|
# FIXME: For now...
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import debug
|
||||||
import utils
|
import utils
|
||||||
from tech import GDS, layer, parameter, drc
|
from tech import GDS, layer, parameter, drc
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
from globals import OPTS
|
|
||||||
import logical_effort
|
import logical_effort
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,12 +27,24 @@ class sense_amp(design.design):
|
||||||
props.sense_amp.pin.vdd,
|
props.sense_amp.pin.vdd,
|
||||||
props.sense_amp.pin.gnd]
|
props.sense_amp.pin.gnd]
|
||||||
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
if not OPTS.netlist_only:
|
cell_size_layer = "boundary"
|
||||||
(width, height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
|
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"])
|
def __init__(self, name="sense_amp"):
|
||||||
else:
|
super().__init__(name)
|
||||||
(width, height) = (0, 0)
|
debug.info(2, "Create sense_amp")
|
||||||
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):
|
def get_bl_names(self):
|
||||||
return props.sense_amp.pin.bl
|
return props.sense_amp.pin.bl
|
||||||
|
|
@ -49,15 +60,6 @@ class sense_amp(design.design):
|
||||||
def en_name(self):
|
def en_name(self):
|
||||||
return props.sense_amp.pin.en
|
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):
|
def get_cin(self):
|
||||||
|
|
||||||
# FIXME: This input load will be applied to both the s_en timing and bitline timing.
|
# FIXME: This input load will be applied to both the s_en timing and bitline timing.
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
import utils
|
import utils
|
||||||
from tech import GDS,layer
|
from tech import GDS, layer
|
||||||
|
|
||||||
|
|
||||||
class tri_gate(design.design):
|
class tri_gate(design.design):
|
||||||
"""
|
"""
|
||||||
|
|
@ -19,8 +20,7 @@ class tri_gate(design.design):
|
||||||
|
|
||||||
pin_names = ["in", "out", "en", "en_bar", "vdd", "gnd"]
|
pin_names = ["in", "out", "en", "en_bar", "vdd", "gnd"]
|
||||||
type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
|
||||||
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
|
cell_size_layer = "boundary"
|
||||||
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"])
|
|
||||||
|
|
||||||
unique_id = 1
|
unique_id = 1
|
||||||
|
|
||||||
|
|
@ -28,12 +28,20 @@ class tri_gate(design.design):
|
||||||
if name=="":
|
if name=="":
|
||||||
name = "tri{0}".format(tri_gate.unique_id)
|
name = "tri{0}".format(tri_gate.unique_id)
|
||||||
tri_gate.unique_id += 1
|
tri_gate.unique_id += 1
|
||||||
design.design.__init__(self, name)
|
super().__init__(self, name)
|
||||||
debug.info(2, "Create tri_gate")
|
debug.info(2, "Create tri_gate")
|
||||||
|
|
||||||
self.width = tri_gate.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = tri_gate.height
|
GDS["unit"],
|
||||||
self.pin_map = tri_gate.pin_map
|
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)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
import utils
|
import utils
|
||||||
from globals import OPTS
|
from tech import GDS, layer
|
||||||
from tech import GDS,layer
|
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
|
|
||||||
|
|
||||||
class write_driver(design.design):
|
class write_driver(design.design):
|
||||||
"""
|
"""
|
||||||
Tristate write driver to be active during write operations only.
|
Tristate write driver to be active during write operations only.
|
||||||
|
|
@ -28,20 +28,23 @@ class write_driver(design.design):
|
||||||
props.write_driver.pin.gnd]
|
props.write_driver.pin.gnd]
|
||||||
|
|
||||||
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||||
if not OPTS.netlist_only:
|
cell_size_layer = "boundary"
|
||||||
(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 = []
|
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
design.design.__init__(self, name)
|
super().__init__(name)
|
||||||
debug.info(2, "Create write_driver")
|
debug.info(2, "Create write_driver")
|
||||||
|
|
||||||
self.width = write_driver.width
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
||||||
self.height = write_driver.height
|
GDS["unit"],
|
||||||
self.pin_map = write_driver.pin_map
|
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)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def get_bl_names(self):
|
def get_bl_names(self):
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import os
|
||||||
import inspect
|
import inspect
|
||||||
import globals
|
import globals
|
||||||
import sys
|
import sys
|
||||||
|
import pdb
|
||||||
|
|
||||||
# the debug levels:
|
# the debug levels:
|
||||||
# 0 = minimum output (default)
|
# 0 = minimum output (default)
|
||||||
|
|
@ -26,9 +27,9 @@ def check(check, str):
|
||||||
log("ERROR: file {0}: line {1}: {2}\n".format(
|
log("ERROR: file {0}: line {1}: {2}\n".format(
|
||||||
os.path.basename(filename), line_number, str))
|
os.path.basename(filename), line_number, str))
|
||||||
|
|
||||||
if globals.OPTS.debug_level > 0:
|
if globals.OPTS.debug:
|
||||||
import pdb
|
|
||||||
pdb.set_trace()
|
pdb.set_trace()
|
||||||
|
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,9 +41,9 @@ def error(str, return_value=0):
|
||||||
log("ERROR: file {0}: line {1}: {2}\n".format(
|
log("ERROR: file {0}: line {1}: {2}\n".format(
|
||||||
os.path.basename(filename), line_number, str))
|
os.path.basename(filename), line_number, str))
|
||||||
|
|
||||||
if globals.OPTS.debug_level > 0 and return_value != 0:
|
if globals.OPTS.debug:
|
||||||
import pdb
|
|
||||||
pdb.set_trace()
|
pdb.set_trace()
|
||||||
|
|
||||||
assert return_value == 0
|
assert return_value == 0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -96,7 +97,7 @@ log.create_file = True
|
||||||
|
|
||||||
def info(lev, str):
|
def info(lev, str):
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
if (OPTS.debug_level >= lev):
|
if (OPTS.verbose_level >= lev):
|
||||||
frm = inspect.stack()[1]
|
frm = inspect.stack()[1]
|
||||||
mod = inspect.getmodule(frm[0])
|
mod = inspect.getmodule(frm[0])
|
||||||
# classname = frm.f_globals['__name__']
|
# classname = frm.f_globals['__name__']
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ word_size = 32
|
||||||
num_words = 128
|
num_words = 128
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -7,7 +7,7 @@ num_r_ports = 0
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 1
|
num_w_ports = 1
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
@ -2,14 +2,14 @@ word_size = 2
|
||||||
num_words = 16
|
num_words = 16
|
||||||
|
|
||||||
tech_name = "freepdk45"
|
tech_name = "freepdk45"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [1.0]
|
supply_voltages = [1.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
# nominal_corners_only = True
|
# nominal_corner_only = True
|
||||||
load_scales = [0.5, 1, 4]
|
load_scales = [0.5, 1, 4]
|
||||||
slew_scales = [0.5, 1]
|
slew_scales = [0.5, 1]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ word_size = 2
|
||||||
num_words = 16
|
num_words = 16
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ word_size = 64
|
||||||
num_words = 1024
|
num_words = 1024
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [ 5.0 ]
|
supply_voltages = [ 5.0 ]
|
||||||
temperatures = [ 25 ]
|
temperatures = [ 25 ]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ word_size = 16
|
||||||
num_words = 256
|
num_words = 256
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = False
|
nominal_corner_only = False
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [5.0]
|
supply_voltages = [5.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "freepdk45"
|
tech_name = "freepdk45"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
|
|
@ -7,7 +7,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = True
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
|
@ -7,7 +7,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = True
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
|
@ -9,7 +9,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = True
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
|
@ -9,7 +9,7 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = False
|
route_supplies = False
|
||||||
check_lvsdrc = False
|
check_lvsdrc = False
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -9,11 +9,11 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "sky130"
|
tech_name = "sky130"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = False
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
perimeter_pins = True
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -9,11 +9,11 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "sky130"
|
tech_name = "sky130"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = False
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
perimeter_pins = True
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -9,11 +9,11 @@ num_r_ports = 1
|
||||||
num_w_ports = 0
|
num_w_ports = 0
|
||||||
|
|
||||||
tech_name = "sky130"
|
tech_name = "sky130"
|
||||||
nominal_corners_only = True
|
nominal_corner_only = True
|
||||||
|
|
||||||
route_supplies = True
|
route_supplies = False
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
perimeter_pins = True
|
perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
#analytical_delay = False
|
#analytical_delay = False
|
||||||
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size,
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -63,7 +63,7 @@ def parse_args():
|
||||||
optparse.make_option("-v",
|
optparse.make_option("-v",
|
||||||
"--verbose",
|
"--verbose",
|
||||||
action="count",
|
action="count",
|
||||||
dest="debug_level",
|
dest="verbose_level",
|
||||||
help="Increase the verbosity level"),
|
help="Increase the verbosity level"),
|
||||||
optparse.make_option("-t",
|
optparse.make_option("-t",
|
||||||
"--tech",
|
"--tech",
|
||||||
|
|
@ -83,11 +83,16 @@ def parse_args():
|
||||||
action="store_false",
|
action="store_false",
|
||||||
dest="analytical_delay",
|
dest="analytical_delay",
|
||||||
help="Perform characterization to calculate delays (default is analytical models)"),
|
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",
|
optparse.make_option("-d",
|
||||||
"--dontpurge",
|
"--debug",
|
||||||
action="store_false",
|
action="store_true",
|
||||||
dest="purge_temp",
|
dest="debug",
|
||||||
help="Don't purge the contents of the temp directory after a successful run")
|
help="Run in debug mode to drop to pdb on failure")
|
||||||
# -h --help is implicit.
|
# -h --help is implicit.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +148,7 @@ def check_versions():
|
||||||
major_required = 3
|
major_required = 3
|
||||||
minor_required = 5
|
minor_required = 5
|
||||||
if not (major_python_version == major_required and minor_python_version >= minor_required):
|
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??
|
# FIXME: Check versions of other tools here??
|
||||||
# or, this could be done in each module (e.g. verify, characterizer, etc.)
|
# or, this could be done in each module (e.g. verify, characterizer, etc.)
|
||||||
|
|
@ -152,7 +157,7 @@ def check_versions():
|
||||||
try:
|
try:
|
||||||
import coverage
|
import coverage
|
||||||
OPTS.coverage = 1
|
OPTS.coverage = 1
|
||||||
except:
|
except ModuleNotFoundError:
|
||||||
OPTS.coverage = 0
|
OPTS.coverage = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -189,6 +194,9 @@ def init_openram(config_file, is_unit_test=True):
|
||||||
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
|
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Setup correct bitcell names
|
||||||
|
setup_bitcell()
|
||||||
|
|
||||||
# Import these to find the executables for checkpointing
|
# Import these to find the executables for checkpointing
|
||||||
import characterizer
|
import characterizer
|
||||||
import verify
|
import verify
|
||||||
|
|
@ -202,17 +210,12 @@ def setup_bitcell():
|
||||||
"""
|
"""
|
||||||
Determine the correct custom or parameterized bitcell for the design.
|
Determine the correct custom or parameterized bitcell for the design.
|
||||||
"""
|
"""
|
||||||
global OPTS
|
|
||||||
|
|
||||||
# If we have non-1rw ports,
|
# If we have non-1rw ports,
|
||||||
# and the user didn't over-ride the bitcell manually,
|
# and the user didn't over-ride the bitcell manually,
|
||||||
# figure out the right bitcell to use
|
# 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):
|
if (OPTS.num_rw_ports == 1 and OPTS.num_w_ports == 0 and OPTS.num_r_ports == 0):
|
||||||
OPTS.bitcell = "bitcell"
|
OPTS.bitcell = "bitcell"
|
||||||
OPTS.replica_bitcell = "replica_bitcell"
|
|
||||||
OPTS.dummy_bitcell = "dummy_bitcell"
|
|
||||||
else:
|
else:
|
||||||
ports = ""
|
ports = ""
|
||||||
if OPTS.num_rw_ports > 0:
|
if OPTS.num_rw_ports > 0:
|
||||||
|
|
@ -226,6 +229,13 @@ def setup_bitcell():
|
||||||
OPTS.bitcell_suffix = "_" + ports
|
OPTS.bitcell_suffix = "_" + ports
|
||||||
OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix
|
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
|
# See if bitcell exists
|
||||||
try:
|
try:
|
||||||
__import__(OPTS.bitcell)
|
__import__(OPTS.bitcell)
|
||||||
|
|
@ -234,6 +244,8 @@ def setup_bitcell():
|
||||||
# or its custom replica bitcell
|
# or its custom replica bitcell
|
||||||
# Use the pbitcell (and give a warning if not in unit test mode)
|
# Use the pbitcell (and give a warning if not in unit test mode)
|
||||||
OPTS.bitcell = "pbitcell"
|
OPTS.bitcell = "pbitcell"
|
||||||
|
OPTS.dummy_bitcell = "dummy_pbitcell"
|
||||||
|
OPTS.replica_bitcell = "replica_pbitcell"
|
||||||
if not OPTS.is_unit_test:
|
if not OPTS.is_unit_test:
|
||||||
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
|
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
|
||||||
debug.info(1, "Using bitcell: {}".format(OPTS.bitcell))
|
debug.info(1, "Using bitcell: {}".format(OPTS.bitcell))
|
||||||
|
|
@ -250,8 +262,8 @@ def get_tool(tool_type, preferences, default_name=None):
|
||||||
if default_name:
|
if default_name:
|
||||||
exe_name = find_exe(default_name)
|
exe_name = find_exe(default_name)
|
||||||
if exe_name == None:
|
if exe_name == None:
|
||||||
debug.error("{0} not found. Cannot find {1} tool.".format(default_name,
|
debug.error("{0} not found. Cannot find {1} tool.".format(default_name, tool_type)
|
||||||
tool_type),
|
+ "Disable DRC/LVS with check_lvsdrc=False to ignore.",
|
||||||
2)
|
2)
|
||||||
else:
|
else:
|
||||||
debug.info(1, "Using {0}: {1}".format(tool_type, exe_name))
|
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)
|
return(name, exe_name)
|
||||||
else:
|
else:
|
||||||
debug.info(1,
|
debug.info(1,
|
||||||
"Could not find {0}, trying next {1} tool.".format(name,
|
"Could not find {0}, trying next {1} tool.".format(name, tool_type))
|
||||||
tool_type))
|
|
||||||
else:
|
else:
|
||||||
return(None, "")
|
return(None, "")
|
||||||
|
|
||||||
|
|
@ -303,7 +314,7 @@ def read_config(config_file, is_unit_test=True):
|
||||||
try:
|
try:
|
||||||
config = importlib.import_module(module_name)
|
config = importlib.import_module(module_name)
|
||||||
except:
|
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 = {}
|
OPTS.overridden = {}
|
||||||
for k, v in config.__dict__.items():
|
for k, v in config.__dict__.items():
|
||||||
|
|
@ -360,7 +371,7 @@ def cleanup_paths():
|
||||||
We should clean up the temp directory after execution.
|
We should clean up the temp directory after execution.
|
||||||
"""
|
"""
|
||||||
global OPTS
|
global OPTS
|
||||||
if not OPTS.purge_temp:
|
if OPTS.keep_temp:
|
||||||
debug.info(0,
|
debug.info(0,
|
||||||
"Preserving temp directory: {}".format(OPTS.openram_temp))
|
"Preserving temp directory: {}".format(OPTS.openram_temp))
|
||||||
return
|
return
|
||||||
|
|
@ -458,7 +469,7 @@ def set_default_corner():
|
||||||
if OPTS.nominal_corner_only:
|
if OPTS.nominal_corner_only:
|
||||||
OPTS.process_corners = ["TT"]
|
OPTS.process_corners = ["TT"]
|
||||||
else:
|
else:
|
||||||
OPTS.process_corners = tech.spice["fet_models"].keys()
|
OPTS.process_corners = list(tech.spice["fet_models"].keys())
|
||||||
|
|
||||||
if (OPTS.supply_voltages == ""):
|
if (OPTS.supply_voltages == ""):
|
||||||
if OPTS.nominal_corner_only:
|
if OPTS.nominal_corner_only:
|
||||||
|
|
@ -528,12 +539,12 @@ def print_time(name, now_time, last_time=None, indentation=2):
|
||||||
global OPTS
|
global OPTS
|
||||||
|
|
||||||
# Don't print during testing
|
# 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:
|
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:
|
else:
|
||||||
time = now_time.strftime('%m/%d/%Y %H:%M:%S')
|
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():
|
def report_status():
|
||||||
|
|
@ -600,4 +611,4 @@ def report_status():
|
||||||
if OPTS.trim_netlist:
|
if OPTS.trim_netlist:
|
||||||
debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).")
|
debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).")
|
||||||
if OPTS.nominal_corner_only:
|
if OPTS.nominal_corner_only:
|
||||||
debug.print_raw("Only characterizing nominal corner.")
|
debug.print_raw("Only generating nominal corner timing.")
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,10 @@ import debug
|
||||||
import design
|
import design
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from math import log, ceil, floor
|
from math import log, ceil, floor
|
||||||
from tech import drc, layer
|
from tech import drc
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class bank(design.design):
|
class bank(design.design):
|
||||||
|
|
@ -906,18 +907,13 @@ class bank(design.design):
|
||||||
offset=mid2)
|
offset=mid2)
|
||||||
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
|
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
|
||||||
|
|
||||||
|
|
||||||
def route_column_address_lines(self, port):
|
def route_column_address_lines(self, port):
|
||||||
""" Connecting the select lines of column mux to the address bus """
|
""" Connecting the select lines of column mux to the address bus """
|
||||||
if not self.col_addr_size>0:
|
if not self.col_addr_size>0:
|
||||||
return
|
return
|
||||||
|
|
||||||
if OPTS.tech_name == "sky130":
|
stack = getattr(self, layer_props.bank.stack)
|
||||||
stack = self.m2_stack
|
pitch = getattr(self, layer_props.bank.pitch)
|
||||||
pitch = self.m3_pitch
|
|
||||||
else:
|
|
||||||
stack = self.m1_stack
|
|
||||||
pitch = self.m2_pitch
|
|
||||||
|
|
||||||
if self.col_addr_size == 1:
|
if self.col_addr_size == 1:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ class bitcell_array(bitcell_base_array):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" 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)
|
self.add_mod(self.cell)
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import debug
|
||||||
import design
|
import design
|
||||||
from tech import cell_properties
|
from tech import cell_properties
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class bitcell_base_array(design.design):
|
class bitcell_base_array(design.design):
|
||||||
|
|
@ -24,7 +25,7 @@ class bitcell_base_array(design.design):
|
||||||
self.column_offset = column_offset
|
self.column_offset = column_offset
|
||||||
|
|
||||||
# Bitcell for port names only
|
# 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.wordline_names = [[] for port in self.all_ports]
|
||||||
self.all_wordline_names = []
|
self.all_wordline_names = []
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class col_cap_array(bitcell_base_array):
|
||||||
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
||||||
self.add_mod(self.dummy_cell)
|
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):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ from tech import layer, preferred_directions
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
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):
|
class column_mux_array(design.design):
|
||||||
|
|
@ -33,14 +34,18 @@ class column_mux_array(design.design):
|
||||||
self.bitcell_br = bitcell_br
|
self.bitcell_br = bitcell_br
|
||||||
self.column_offset = column_offset
|
self.column_offset = column_offset
|
||||||
|
|
||||||
if OPTS.tech_name == "sky130":
|
self.sel_layer = layer_props.column_mux_array.select_layer
|
||||||
self.sel_layer = "m3"
|
self.sel_pitch = getattr(self, layer_props.column_mux_array.select_pitch)
|
||||||
self.sel_pitch = self.m3_pitch
|
self.bitline_layer = layer_props.column_mux_array.bitline_layer
|
||||||
self.bitline_layer = "m1"
|
|
||||||
else:
|
# if OPTS.tech_name == "sky130":
|
||||||
self.sel_layer = "m1"
|
# self.sel_layer = "m3"
|
||||||
self.sel_pitch = self.m2_pitch
|
# self.sel_pitch = self.m3_pitch
|
||||||
self.bitline_layer = "m2"
|
# 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":
|
if preferred_directions[self.sel_layer] == "V":
|
||||||
self.via_directions = ("H", "H")
|
self.via_directions = ("H", "H")
|
||||||
|
|
@ -123,7 +128,7 @@ class column_mux_array(design.design):
|
||||||
|
|
||||||
# For every column, add a pass gate
|
# For every column, add a pass gate
|
||||||
for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
|
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"
|
mirror = "MY"
|
||||||
xoffset = xoffset + self.mux.width
|
xoffset = xoffset + self.mux.width
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ class control_logic(design.design):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add all the required modules """
|
""" Add all the required modules """
|
||||||
|
|
||||||
dff = factory.create(module_type="dff_buf")
|
self.dff = factory.create(module_type="dff_buf")
|
||||||
dff_height = dff.height
|
dff_height = self.dff.height
|
||||||
|
|
||||||
self.ctrl_dff_array = factory.create(module_type="dff_buf_array",
|
self.ctrl_dff_array = factory.create(module_type="dff_buf_array",
|
||||||
rows=self.num_control_signals,
|
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
|
# We will use the maximum since this same value is used to size the wl_en
|
||||||
# and the p_en_bar drivers
|
# 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
|
# wl_en drives every row in the bank
|
||||||
self.wl_en_driver = factory.create(module_type="pdriver",
|
self.wl_en_driver = factory.create(module_type="pdriver",
|
||||||
|
|
@ -721,10 +721,8 @@ class control_logic(design.design):
|
||||||
def route_supply(self):
|
def route_supply(self):
|
||||||
""" Add vdd and gnd to the instance cells """
|
""" Add vdd and gnd to the instance cells """
|
||||||
|
|
||||||
if OPTS.tech_name == "sky130":
|
supply_layer = self.dff.get_pin("vdd").layer
|
||||||
supply_layer = "li"
|
|
||||||
else:
|
|
||||||
supply_layer = "m1"
|
|
||||||
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
|
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
|
||||||
for inst in self.row_end_inst:
|
for inst in self.row_end_inst:
|
||||||
pins = inst.get_pins("vdd")
|
pins = inst.get_pins("vdd")
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import parameter, layer
|
from tech import layer
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
@ -23,7 +23,6 @@ class dff_buf(design.design):
|
||||||
unique_id = 1
|
unique_id = 1
|
||||||
|
|
||||||
def __init__(self, inv1_size=2, inv2_size=4, name=""):
|
def __init__(self, inv1_size=2, inv2_size=4, name=""):
|
||||||
|
|
||||||
if name=="":
|
if name=="":
|
||||||
name = "dff_buf_{0}".format(dff_buf.unique_id)
|
name = "dff_buf_{0}".format(dff_buf.unique_id)
|
||||||
dff_buf.unique_id += 1
|
dff_buf.unique_id += 1
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ class dummy_array(bitcell_base_array):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" Add the modules used in this design """
|
||||||
|
|
||||||
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
|
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
|
||||||
self.cell = factory.create(module_type="bitcell")
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||||
self.add_mod(self.dummy_cell)
|
self.add_mod(self.dummy_cell)
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import math
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_decoder(design.design):
|
class hierarchical_decoder(design.design):
|
||||||
|
|
@ -26,7 +27,7 @@ class hierarchical_decoder(design.design):
|
||||||
self.pre3x8_inst = []
|
self.pre3x8_inst = []
|
||||||
self.pre4x16_inst = []
|
self.pre4x16_inst = []
|
||||||
|
|
||||||
b = factory.create(module_type="bitcell")
|
b = factory.create(module_type=OPTS.bitcell)
|
||||||
self.cell_height = b.height
|
self.cell_height = b.height
|
||||||
|
|
||||||
self.num_outputs = num_outputs
|
self.num_outputs = num_outputs
|
||||||
|
|
@ -181,24 +182,14 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
# Inputs to cells are on input layer
|
# Inputs to cells are on input layer
|
||||||
# Outputs from cells are on output layer
|
# Outputs from cells are on output layer
|
||||||
if OPTS.tech_name == "sky130":
|
|
||||||
self.bus_layer = "m1"
|
self.bus_layer = layer_props.hierarchical_decoder.bus_layer
|
||||||
self.bus_directions = "nonpref"
|
self.bus_directions = layer_props.hierarchical_decoder.bus_directions
|
||||||
self.bus_pitch = self.m1_pitch
|
self.bus_pitch = getattr(self, self.bus_layer + "_pitch")
|
||||||
self.bus_space = self.m2_space
|
self.bus_space = getattr(self, self.bus_layer + "_space")
|
||||||
self.input_layer = "m2"
|
self.input_layer = layer_props.hierarchical_decoder.input_layer
|
||||||
self.output_layer = "li"
|
self.output_layer = layer_props.hierarchical_decoder.output_layer
|
||||||
self.output_layer_pitch = self.li_pitch
|
self.output_layer_pitch = getattr(self, self.output_layer + "_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
|
|
||||||
|
|
||||||
# Two extra pitches between modules on left and right
|
# 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
|
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.
|
must-connects next level up.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if OPTS.tech_name == "sky130":
|
if layer_props.hierarchical_decoder.vertical_supply:
|
||||||
for n in ["vdd", "gnd"]:
|
for n in ["vdd", "gnd"]:
|
||||||
pins = self.and_inst[0].get_pins(n)
|
pins = self.and_inst[0].get_pins(n)
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
|
|
@ -678,9 +669,9 @@ class hierarchical_decoder(design.design):
|
||||||
mid_point2 = vector(x_offset, y_offset)
|
mid_point2 = vector(x_offset, y_offset)
|
||||||
rail_pos = vector(self.predecode_bus[rail_name].cx(), mid_point2.y)
|
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])
|
self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||||
if OPTS.tech_name == "sky130":
|
if layer_props.hierarchical_decoder.vertical_supply:
|
||||||
above_rail = vector(self.predecode_bus[rail_name].cx(), mid_point2.y + (self.cell_height/2))
|
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)
|
self.add_path(self.bus_layer, [rail_pos, above_rail], width=self.li_width + self.m1_enclose_mcon * 2)
|
||||||
|
|
||||||
# pin_pos = pin.center()
|
# pin_pos = pin.center()
|
||||||
# rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y)
|
# rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import math
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_predecode(design.design):
|
class hierarchical_predecode(design.design):
|
||||||
|
|
@ -20,7 +21,7 @@ class hierarchical_predecode(design.design):
|
||||||
def __init__(self, name, input_number, height=None):
|
def __init__(self, name, input_number, height=None):
|
||||||
self.number_of_inputs = input_number
|
self.number_of_inputs = input_number
|
||||||
|
|
||||||
b = factory.create(module_type="bitcell")
|
b = factory.create(module_type=OPTS.bitcell)
|
||||||
|
|
||||||
if not height:
|
if not height:
|
||||||
self.cell_height = b.height
|
self.cell_height = b.height
|
||||||
|
|
@ -83,23 +84,14 @@ class hierarchical_predecode(design.design):
|
||||||
|
|
||||||
# Inputs to cells are on input layer
|
# Inputs to cells are on input layer
|
||||||
# Outputs from cells are on output layer
|
# Outputs from cells are on output layer
|
||||||
if OPTS.tech_name == "sky130":
|
|
||||||
self.bus_layer = "m1"
|
self.bus_layer = layer_props.hierarchical_predecode.bus_layer
|
||||||
self.bus_directions = "nonpref"
|
self.bus_directions = layer_props.hierarchical_predecode.bus_directions
|
||||||
self.bus_pitch = self.m1_pitch
|
self.bus_pitch = getattr(self, self.bus_layer + "_pitch")
|
||||||
self.bus_space = 1.5 * self.m1_space
|
self.bus_space = layer_props.hierarchical_predecode.bus_space_factor * getattr(self, self.bus_layer + "_space")
|
||||||
self.input_layer = "m2"
|
self.input_layer = layer_props.hierarchical_predecode.input_layer
|
||||||
self.output_layer = "li"
|
self.output_layer = layer_props.hierarchical_predecode.output_layer
|
||||||
self.output_layer_pitch = self.li_pitch
|
self.output_layer_pitch = getattr(self, self.output_layer + "_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.height = self.number_of_outputs * self.and_mod.height
|
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,
|
height=via.mod.second_layer_height,
|
||||||
width=via.mod.second_layer_width)
|
width=via.mod.second_layer_width)
|
||||||
|
|
||||||
if OPTS.tech_name == "sky130":
|
if layer_props.hierarchical_predecode.vertical_supply:
|
||||||
below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height/2))
|
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)
|
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):
|
def route_and_to_rails(self):
|
||||||
# This 2D array defines the connection mapping
|
# This 2D array defines the connection mapping
|
||||||
|
|
@ -319,8 +311,8 @@ class hierarchical_predecode(design.design):
|
||||||
def route_vdd_gnd(self):
|
def route_vdd_gnd(self):
|
||||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
""" 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
|
# We may ahve vertical power supply rails
|
||||||
if OPTS.tech_name == "sky130" and not self.column_decoder:
|
if layer_props.hierarchical_predecode.vertical_supply and not self.column_decoder:
|
||||||
for n in ["vdd", "gnd"]:
|
for n in ["vdd", "gnd"]:
|
||||||
# This makes a wire from top to bottom for both inv and and gates
|
# This makes a wire from top to bottom for both inv and and gates
|
||||||
for i in [self.inv_inst, self.and_inst]:
|
for i in [self.inv_inst, self.and_inst]:
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" Add the modules used in this design """
|
||||||
# This is just used for names
|
# 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",
|
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
||||||
cols=self.cols,
|
cols=self.cols,
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class bitcell_array(bitcell_base_array):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" 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)
|
self.add_mod(self.cell)
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from tech import layer
|
from tech import layer
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class port_address(design.design):
|
class port_address(design.design):
|
||||||
|
|
@ -80,7 +81,7 @@ class port_address(design.design):
|
||||||
self.copy_power_pins(inst, "gnd")
|
self.copy_power_pins(inst, "gnd")
|
||||||
|
|
||||||
for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"):
|
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())
|
self.add_power_pin("vdd", rbl_vdd_pin.center())
|
||||||
else:
|
else:
|
||||||
self.add_power_pin("vdd", rbl_vdd_pin.lc())
|
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
|
# The polarity must be switched if we have a hierarchical wordline
|
||||||
# to compensate for the local array inverters
|
# 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:
|
if local_array_size > 0:
|
||||||
self.rbl_driver = factory.create(module_type="inv_dec",
|
self.rbl_driver = factory.create(module_type="inv_dec",
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from sram_factory import factory
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class port_data(design.design):
|
class port_data(design.design):
|
||||||
|
|
@ -32,7 +33,7 @@ class port_data(design.design):
|
||||||
self.num_spare_cols = 0
|
self.num_spare_cols = 0
|
||||||
|
|
||||||
if not bit_offsets:
|
if not bit_offsets:
|
||||||
bitcell = factory.create(module_type="bitcell")
|
bitcell = factory.create(module_type=OPTS.bitcell)
|
||||||
self.bit_offsets = []
|
self.bit_offsets = []
|
||||||
for i in range(self.num_cols + self.num_spare_cols):
|
for i in range(self.num_cols + self.num_spare_cols):
|
||||||
self.bit_offsets.append(i * bitcell.width)
|
self.bit_offsets.append(i * bitcell.width)
|
||||||
|
|
@ -190,7 +191,7 @@ class port_data(design.design):
|
||||||
# and mirroring happens correctly
|
# and mirroring happens correctly
|
||||||
|
|
||||||
# Used for names/dimensions only
|
# Used for names/dimensions only
|
||||||
self.cell = factory.create(module_type="bitcell")
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||||
|
|
||||||
if self.port == 0:
|
if self.port == 0:
|
||||||
# Append an offset on the left
|
# 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,
|
# create arrays of bitline and bitline_bar names for read,
|
||||||
# write, or all ports
|
# 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.bl_names = self.bitcell.get_all_bl_names()
|
||||||
self.br_names = self.bitcell.get_all_br_names()
|
self.br_names = self.bitcell.get_all_br_names()
|
||||||
self.wl_names = self.bitcell.get_all_wl_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,
|
inst1_start_bit=self.num_cols + off,
|
||||||
inst2_start_bit=self.word_size)
|
inst2_start_bit=self.word_size)
|
||||||
|
|
||||||
# This could be a channel route, but in some techs the bitlines
|
elif layer_props.port_data.channel_route_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:
|
|
||||||
self.channel_route_bitlines(inst1=inst1,
|
self.channel_route_bitlines(inst1=inst1,
|
||||||
inst1_bls_template=inst1_bls_templ,
|
inst1_bls_template=inst1_bls_templ,
|
||||||
inst2=inst2,
|
inst2=inst2,
|
||||||
num_bits=self.word_size + self.num_spare_cols,
|
num_bits=self.word_size + self.num_spare_cols,
|
||||||
inst1_start_bit=start_bit)
|
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):
|
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 """
|
""" 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
|
# This could be a channel route, but in some techs the bitlines
|
||||||
# are too close together.
|
# 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,
|
self.connect_bitlines(inst1=inst1, inst2=inst2,
|
||||||
num_bits=self.word_size,
|
num_bits=self.word_size,
|
||||||
inst1_bls_template=inst1_bls_templ,
|
inst1_bls_template=inst1_bls_templ,
|
||||||
inst1_start_bit=start_bit)
|
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):
|
def route_write_driver_to_sense_amp(self, port):
|
||||||
""" Routing of BL and BR between write driver and sense amp """
|
""" Routing of BL and BR between write driver and sense amp """
|
||||||
|
|
|
||||||
|
|
@ -281,7 +281,7 @@ class replica_bitcell_array(bitcell_base_array):
|
||||||
self.supplies = ["vdd", "gnd"]
|
self.supplies = ["vdd", "gnd"]
|
||||||
|
|
||||||
# Used for names/dimensions only
|
# Used for names/dimensions only
|
||||||
self.cell = factory.create(module_type="bitcell")
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||||
|
|
||||||
# Main array
|
# Main array
|
||||||
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from tech import cell_properties
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class replica_column(bitcell_base_array):
|
class replica_column(bitcell_base_array):
|
||||||
|
|
@ -41,7 +42,7 @@ class replica_column(bitcell_base_array):
|
||||||
"Replica bit cannot be the dummy row.")
|
"Replica bit cannot be the dummy row.")
|
||||||
debug.check(replica_bit <= self.left_rbl or replica_bit >= self.total_size - self.right_rbl - 1,
|
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.")
|
"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,
|
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"
|
"sky130 currently requires rows to be even and to start with X mirroring"
|
||||||
+ " (left_rbl must be odd) for LVS.")
|
+ " (left_rbl must be odd) for LVS.")
|
||||||
|
|
@ -76,9 +77,9 @@ class replica_column(bitcell_base_array):
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def add_modules(self):
|
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.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)
|
self.add_mod(self.dummy_cell)
|
||||||
try:
|
try:
|
||||||
edge_module_type = ("col_cap" if cell_properties.bitcell.end_caps else "dummy")
|
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.edge_cell = factory.create(module_type=edge_module_type + "_" + OPTS.bitcell)
|
||||||
self.add_mod(self.edge_cell)
|
self.add_mod(self.edge_cell)
|
||||||
# Used for pin names only
|
# Used for pin names only
|
||||||
self.cell = factory.create(module_type="bitcell")
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
self.cell_inst = {}
|
self.cell_inst = {}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class row_cap_array(bitcell_base_array):
|
||||||
self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell))
|
self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell))
|
||||||
self.add_mod(self.dummy_cell)
|
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):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ class sense_amp_array(design.design):
|
||||||
|
|
||||||
# This is just used for measurements,
|
# This is just used for measurements,
|
||||||
# so don't add the module
|
# 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):
|
def create_sense_amp_array(self):
|
||||||
self.local_insts = []
|
self.local_insts = []
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import drc, layer
|
from tech import layer
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class wordline_buffer_array(design.design):
|
class wordline_buffer_array(design.design):
|
||||||
|
|
@ -58,7 +59,7 @@ class wordline_buffer_array(design.design):
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def add_modules(self):
|
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",
|
self.wl_driver = factory.create(module_type="inv_dec",
|
||||||
size=self.cols,
|
size=self.cols,
|
||||||
|
|
@ -70,7 +71,7 @@ class wordline_buffer_array(design.design):
|
||||||
Add a pin for each row of vdd/gnd which
|
Add a pin for each row of vdd/gnd which
|
||||||
are must-connects next level up.
|
are must-connects next level up.
|
||||||
"""
|
"""
|
||||||
if OPTS.tech_name == "sky130":
|
if layer_props.wordline_driver.vertical_supply:
|
||||||
for name in ["vdd", "gnd"]:
|
for name in ["vdd", "gnd"]:
|
||||||
supply_pins = self.wld_inst[0].get_pins(name)
|
supply_pins = self.wld_inst[0].get_pins(name)
|
||||||
for pin in supply_pins:
|
for pin in supply_pins:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from tech import drc, layer
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class wordline_driver_array(design.design):
|
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
|
Add a pin for each row of vdd/gnd which
|
||||||
are must-connects next level up.
|
are must-connects next level up.
|
||||||
"""
|
"""
|
||||||
if OPTS.tech_name == "sky130":
|
if layer_props.wordline_driver.vertical_supply:
|
||||||
for name in ["vdd", "gnd"]:
|
for name in ["vdd", "gnd"]:
|
||||||
supply_pins = self.wld_inst[0].get_pins(name)
|
supply_pins = self.wld_inst[0].get_pins(name)
|
||||||
for pin in supply_pins:
|
for pin in supply_pins:
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ class write_driver_array(design.design):
|
||||||
|
|
||||||
# This is just used for measurements,
|
# This is just used for measurements,
|
||||||
# so don't add the module
|
# 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):
|
def create_write_array(self):
|
||||||
self.driver_insts = []
|
self.driver_insts = []
|
||||||
|
|
|
||||||
|
|
@ -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 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
|
# 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")
|
self.driver = factory.create(module_type="write_driver")
|
||||||
if self.bitcell.width > self.driver.width:
|
if self.bitcell.width > self.driver.width:
|
||||||
self.driver_spacing = self.bitcell.width
|
self.driver_spacing = self.bitcell.width
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,10 @@ class options(optparse.Values):
|
||||||
os.getpid())
|
os.getpid())
|
||||||
# This is the verbosity level to control debug information. 0 is none, 1
|
# This is the verbosity level to control debug information. 0 is none, 1
|
||||||
# is minimal, etc.
|
# is minimal, etc.
|
||||||
debug_level = 0
|
verbose_level = 0
|
||||||
|
# Drop to pdb on failure?
|
||||||
|
debug = False
|
||||||
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# Run-time vs accuracy options.
|
# Run-time vs accuracy options.
|
||||||
|
|
@ -137,7 +140,8 @@ class options(optparse.Values):
|
||||||
# Route the input/output pins to the perimeter
|
# Route the input/output pins to the perimeter
|
||||||
perimeter_pins = False
|
perimeter_pins = False
|
||||||
|
|
||||||
purge_temp = True
|
keep_temp = False
|
||||||
|
|
||||||
|
|
||||||
# These are the default modules that can be over-riden
|
# These are the default modules that can be over-riden
|
||||||
bitcell_suffix = ""
|
bitcell_suffix = ""
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import debug
|
||||||
from tech import drc, layer
|
from tech import drc, layer
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
from tech import cell_properties as cell_props
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -64,7 +65,7 @@ class column_mux(pgate.pgate):
|
||||||
self.add_pn_wells()
|
self.add_pn_wells()
|
||||||
|
|
||||||
def add_ptx(self):
|
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
|
# Adds nmos_lower,nmos_upper to the module
|
||||||
self.ptx_width = self.tx_size * drc("minwidth_tx")
|
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))
|
+ vector(0, self.nmos.active_height + max(self.active_space, self.poly_space))
|
||||||
self.nmos_upper.place(nmos_upper_position)
|
self.nmos_upper.place(nmos_upper_position)
|
||||||
|
|
||||||
if OPTS.tech_name == "sky130":
|
if cell_props.pgate.add_implants:
|
||||||
self.add_implants()
|
self.extend_implants()
|
||||||
|
|
||||||
def connect_poly(self):
|
def connect_poly(self):
|
||||||
""" Connect the poly gate of the two pass transistors """
|
""" 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],
|
self.add_path(self.col_mux_stack[2],
|
||||||
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()])
|
[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.
|
Add top-to-bottom implants for adjacency issues in s8.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -146,4 +146,3 @@ class pand2(pgate.pgate):
|
||||||
offset=pin.center(),
|
offset=pin.center(),
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=pin.height())
|
height=pin.height())
|
||||||
|
|
||||||
|
|
@ -161,4 +161,3 @@ class pand3(pgate.pgate):
|
||||||
slew=nand_delay.slew,
|
slew=nand_delay.slew,
|
||||||
load=load)
|
load=load)
|
||||||
return nand_delay + inv_delay
|
return nand_delay + inv_delay
|
||||||
|
|
||||||
|
|
@ -168,4 +168,3 @@ class pdriver(pgate.pgate):
|
||||||
def get_sizes(self):
|
def get_sizes(self):
|
||||||
""" Return the relative sizes of the buffers """
|
""" Return the relative sizes of the buffers """
|
||||||
return self.size_list
|
return self.size_list
|
||||||
|
|
||||||
|
|
@ -13,8 +13,8 @@ from bisect import bisect_left
|
||||||
from tech import layer, drc
|
from tech import layer, drc
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from tech import cell_properties as cell_props
|
||||||
if(OPTS.tech_name == "sky130"):
|
if cell_props.ptx.bin_spice_models:
|
||||||
from tech import nmos_bins, pmos_bins
|
from tech import nmos_bins, pmos_bins
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -192,7 +192,7 @@ class pgate(design.design):
|
||||||
width=self.width + 2 * self.well_extend_active,
|
width=self.width + 2 * self.well_extend_active,
|
||||||
height=pwell_height)
|
height=pwell_height)
|
||||||
|
|
||||||
if OPTS.tech_name == "sky130":
|
if cell_props.pgate.add_implants:
|
||||||
self.extend_implants()
|
self.extend_implants()
|
||||||
|
|
||||||
def add_nwell_contact(self, pmos, pmos_pos):
|
def add_nwell_contact(self, pmos, pmos_pos):
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue