Merge branch 'dev' into automated_analytical_model

This commit is contained in:
Hunter Nichols 2021-06-09 15:45:41 -07:00
commit 4ec2e1240f
34 changed files with 423 additions and 190 deletions

View File

@ -176,7 +176,7 @@ class cell_properties():
self.names["col_cap_bitcell_2port"] = "col_cap_cell_2rw" self.names["col_cap_bitcell_2port"] = "col_cap_cell_2rw"
self.names["row_cap_bitcell_1port"] = "row_cap_cell_1rw" self.names["row_cap_bitcell_1port"] = "row_cap_cell_1rw"
self.names["row_cap_bitcell_2port"] = "row_cap_cell_2rw" self.names["row_cap_bitcell_2port"] = "row_cap_cell_2rw"
self.use_strap = False
self._ptx = _ptx(model_is_subckt=False, self._ptx = _ptx(model_is_subckt=False,
bin_spice_models=False) bin_spice_models=False)

View File

@ -48,7 +48,8 @@ class design(hierarchy_design):
self.add_pin_indices(prop.port_indices) self.add_pin_indices(prop.port_indices)
self.add_pin_names(prop.port_map) self.add_pin_names(prop.port_map)
self.add_pin_types(prop.port_types) self.add_pin_types(prop.port_types)
(width, height) = utils.get_libcell_size(self.cell_name, (width, height) = utils.get_libcell_size(self.cell_name,
GDS["unit"], GDS["unit"],
layer[prop.boundary_layer]) layer[prop.boundary_layer])

View File

@ -41,7 +41,8 @@ class layout():
self.width = None self.width = None
self.height = None self.height = None
self.bounding_box = None self.bounding_box = None # The rectangle shape
self.bbox = None # The ll, ur coords
# Holds module/cell layout instances # Holds module/cell layout instances
self.insts = [] self.insts = []
# Set of names to check for duplicates # Set of names to check for duplicates
@ -1163,6 +1164,57 @@ class layout():
self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()] self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()]
def get_bbox(self, side="all", big_margin=0, little_margin=0):
"""
Get the bounding box from the GDS
"""
gds_filename = OPTS.openram_temp + "temp.gds"
# If didn't specify a gds blockage file, write it out to read the gds
# This isn't efficient, but easy for now
# Load the gds file and read in all the shapes
self.gds_write(gds_filename)
layout = gdsMill.VlsiLayout(units=GDS["unit"])
reader = gdsMill.Gds2reader(layout)
reader.loadFromFile(gds_filename)
top_name = layout.rootStructureName
if not self.bbox:
# The boundary will determine the limits to the size
# of the routing grid
boundary = layout.measureBoundary(top_name)
# These must be un-indexed to get rid of the matrix type
ll = vector(boundary[0][0], boundary[0][1])
ur = vector(boundary[1][0], boundary[1][1])
else:
ll, ur = self.bbox
ll_offset = vector(0, 0)
ur_offset = vector(0, 0)
if side in ["ring", "top", "all"]:
ur_offset += vector(0, big_margin)
else:
ur_offset += vector(0, little_margin)
if side in ["ring", "bottom", "all"]:
ll_offset += vector(0, big_margin)
else:
ll_offset += vector(0, little_margin)
if side in ["ring", "left", "all"]:
ll_offset += vector(big_margin, 0)
else:
ll_offset += vector(little_margin, 0)
if side in ["ring", "right", "all"]:
ur_offset += vector(big_margin, 0)
else:
ur_offset += vector(little_margin, 0)
bbox = (ll - ll_offset, ur + ur_offset)
size = ur - ll
debug.info(1, "Size: {0} x {1} with perimeter big margin {2} little margin {3}".format(size.x,
size.y,
big_margin,
little_margin))
return bbox
def add_enclosure(self, insts, layer="nwell", extend=0, leftx=None, rightx=None, topy=None, boty=None): def add_enclosure(self, insts, layer="nwell", extend=0, leftx=None, rightx=None, topy=None, boty=None):
""" """
Add a layer that surrounds the given instances. Useful Add a layer that surrounds the given instances. Useful
@ -1205,22 +1257,24 @@ class layout():
height=ymax - ymin) height=ymax - ymin)
return rect return rect
def copy_power_pins(self, inst, name, add_vias=True): def copy_power_pins(self, inst, name, add_vias=True, new_name=""):
""" """
This will copy a power pin if it is on the lowest power_grid layer. This will copy a power pin if it is on the lowest power_grid layer.
If it is on M1, it will add a power via too. If it is on M1, it will add a power via too.
""" """
pins = inst.get_pins(name) pins = inst.get_pins(name)
for pin in pins: for pin in pins:
if new_name == "":
new_name = pin.name
if pin.layer == self.pwr_grid_layer: if pin.layer == self.pwr_grid_layer:
self.add_layout_pin(name, self.add_layout_pin(new_name,
pin.layer, pin.layer,
pin.ll(), pin.ll(),
pin.width(), pin.width(),
pin.height()) pin.height())
elif add_vias: elif add_vias:
self.copy_power_pin(pin) self.copy_power_pin(pin, new_name=new_name)
def add_io_pin(self, instance, pin_name, new_name, start_layer=None): def add_io_pin(self, instance, pin_name, new_name, start_layer=None):
""" """
@ -1266,13 +1320,15 @@ class layout():
width=width, width=width,
height=height) height=height)
def copy_power_pin(self, pin, loc=None, directions=None): def copy_power_pin(self, pin, loc=None, directions=None, new_name=""):
""" """
Add a single power pin from the lowest power_grid layer down to M1 (or li) at Add a single power pin from the lowest power_grid layer down to M1 (or li) at
the given center location. The starting layer is specified to determine the given center location. The starting layer is specified to determine
which vias are needed. which vias are needed.
""" """
if new_name == "":
new_name = pin.name
if not loc: if not loc:
loc = pin.center() loc = pin.center()
@ -1286,7 +1342,7 @@ class layout():
height = None height = None
if pin.layer == self.pwr_grid_layer: if pin.layer == self.pwr_grid_layer:
self.add_layout_pin_rect_center(text=pin.name, self.add_layout_pin_rect_center(text=new_name,
layer=self.pwr_grid_layer, layer=self.pwr_grid_layer,
offset=loc, offset=loc,
width=width, width=width,
@ -1301,7 +1357,7 @@ class layout():
width = via.width width = via.width
if not height: if not height:
height = via.height height = via.height
self.add_layout_pin_rect_center(text=pin.name, self.add_layout_pin_rect_center(text=new_name,
layer=self.pwr_grid_layer, layer=self.pwr_grid_layer,
offset=loc, offset=loc,
width=width, width=width,
@ -1357,7 +1413,7 @@ class layout():
[ll, ur] = bbox [ll, ur] = bbox
# Possibly inflate the bbox # Possibly inflate the bbox
nwell_offset = vector(self.nwell_width, self.nwell_width) nwell_offset = vector(2 * self.nwell_width, 2 * self.nwell_width)
ll -= nwell_offset.scale(inflate, inflate) ll -= nwell_offset.scale(inflate, inflate)
ur += nwell_offset.scale(inflate, inflate) ur += nwell_offset.scale(inflate, inflate)
@ -1396,7 +1452,7 @@ class layout():
to_layer="m1", to_layer="m1",
offset=loc) offset=loc)
else: else:
self.add_power_pin(name="gnd", self.add_power_pin(name="vdd",
loc=loc, loc=loc,
start_layer="li") start_layer="li")
count += 1 count += 1
@ -1416,7 +1472,7 @@ class layout():
to_layer="m1", to_layer="m1",
offset=loc) offset=loc)
else: else:
self.add_power_pin(name="gnd", self.add_power_pin(name="vdd",
loc=loc, loc=loc,
start_layer="li") start_layer="li")
count += 1 count += 1
@ -1436,7 +1492,7 @@ class layout():
to_layer="m2", to_layer="m2",
offset=loc) offset=loc)
else: else:
self.add_power_pin(name="gnd", self.add_power_pin(name="vdd",
loc=loc, loc=loc,
start_layer="li") start_layer="li")
count += 1 count += 1
@ -1456,7 +1512,7 @@ class layout():
to_layer="m2", to_layer="m2",
offset=loc) offset=loc)
else: else:
self.add_power_pin(name="gnd", self.add_power_pin(name="vdd",
loc=loc, loc=loc,
start_layer="li") start_layer="li")
count += 1 count += 1

View File

@ -112,23 +112,25 @@ class lef:
for pin_name in self.pins: for pin_name in self.pins:
pins = self.get_pins(pin_name) pins = self.get_pins(pin_name)
for pin in pins: for pin in pins:
inflated_pin = pin.inflated_pin(multiple=1) inflated_pin = pin.inflated_pin(multiple=2)
another_iteration_needed = True continue_fragmenting = True
while another_iteration_needed: while continue_fragmenting:
another_iteration_needed = False continue_fragmenting = False
old_blockages = list(self.blockages[pin.layer]) old_blockages = list(self.blockages[pin.layer])
for blockage in old_blockages: for blockage in old_blockages:
if blockage.overlaps(inflated_pin): if blockage.overlaps(inflated_pin):
intersection_shape = blockage.intersection(inflated_pin) intersection_shape = blockage.intersection(inflated_pin)
# If it is zero area, don't add the pin # If it is zero area, don't split the blockage
if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]:
continue continue
another_iteration_needed = True
# Remove the old blockage and add the new ones # Remove the old blockage and add the new ones
self.blockages[pin.layer].remove(blockage) self.blockages[pin.layer].remove(blockage)
intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer) intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer)
new_blockages = blockage.cut(intersection_pin) new_blockages = blockage.cut(intersection_pin)
self.blockages[pin.layer].extend(new_blockages) self.blockages[pin.layer].extend(new_blockages)
# We split something so make another pass
continue_fragmenting = True
def lef_write_header(self): def lef_write_header(self):
""" Header of LEF file """ """ Header of LEF file """

View File

@ -606,7 +606,9 @@ class pin_layout:
# Don't add the existing shape in if it overlaps the pin shape # Don't add the existing shape in if it overlaps the pin shape
if new_shape.contains(shape): if new_shape.contains(shape):
continue continue
new_shapes.append(new_shape) # Only add non-zero shapes
if new_shape.area() > 0:
new_shapes.append(new_shape)
return new_shapes return new_shapes

View File

@ -148,12 +148,15 @@ def get_gds_pins(pin_names, name, gds_filename, units):
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 if pin_shape != None:
rect = [vector(boundary[0], boundary[1]), (lpp, boundary) = pin_shape
vector(boundary[2], boundary[3])] rect = [vector(boundary[0], boundary[1]),
# this is a list because other cells/designs vector(boundary[2], boundary[3])]
# may have must-connect pins # this is a list because other cells/designs
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp)) # may have must-connect pins
if isinstance(lpp[1], list):
lpp = (lpp[0], None)
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
_GDS_PINS_CACHE[k] = cell _GDS_PINS_CACHE[k] = cell
return dict(cell) return dict(cell)

View File

@ -24,7 +24,7 @@ debug.info(1, "Initializing characterizer...")
OPTS.spice_exe = "" OPTS.spice_exe = ""
if not OPTS.analytical_delay: if not OPTS.analytical_delay:
if OPTS.spice_name != "": if OPTS.spice_name:
# Capitalize Xyce # Capitalize Xyce
if OPTS.spice_name == "xyce": if OPTS.spice_name == "xyce":
OPTS.spice_name = "Xyce" OPTS.spice_name = "Xyce"
@ -45,7 +45,7 @@ if not OPTS.analytical_delay:
if OPTS.spice_name == "ngspice": if OPTS.spice_name == "ngspice":
os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp) os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp)
if OPTS.spice_exe == "": if not OPTS.spice_exe:
debug.error("No recognizable spice version found. Unable to perform characterization.", 1) debug.error("No recognizable spice version found. Unable to perform characterization.", 1)
else: else:
debug.info(1, "Finding spice simulator: {} ({})".format(OPTS.spice_name, OPTS.spice_exe)) debug.info(1, "Finding spice simulator: {} ({})".format(OPTS.spice_name, OPTS.spice_exe))

View File

@ -276,8 +276,8 @@ class stimuli():
self.sf.write(".OPTIONS MEASURE MEASFAIL=1\n") self.sf.write(".OPTIONS MEASURE MEASFAIL=1\n")
self.sf.write(".OPTIONS LINSOL type=klu\n") self.sf.write(".OPTIONS LINSOL type=klu\n")
self.sf.write(".TRAN {0}p {1}n\n".format(timestep, end_time)) self.sf.write(".TRAN {0}p {1}n\n".format(timestep, end_time))
else: elif OPTS.spice_name:
debug.error("Unkown spice simulator {}".format(OPTS.spice_name)) debug.error("Unkown spice simulator {}".format(OPTS.spice_name), -1)
# create plots for all signals # create plots for all signals
if not OPTS.use_pex: # Don't save all for extracted simulations if not OPTS.use_pex: # Don't save all for extracted simulations

View File

@ -9,8 +9,8 @@ nominal_corner_only = True
# Local wordlines have issues with met3 power routing for now # Local wordlines have issues with met3 power routing for now
#local_array_size = 16 #local_array_size = 16
#route_supplies = "ring" route_supplies = "ring"
route_supplies = "left" #route_supplies = "left"
check_lvsdrc = True check_lvsdrc = True
#perimeter_pins = False #perimeter_pins = False
#netlist_only = True #netlist_only = True

View File

@ -3,7 +3,7 @@ from datetime import *
import numpy as np import numpy as np
import math import math
import debug import debug
from tech import use_purpose
class VlsiLayout: class VlsiLayout:
"""Class represent a hierarchical layout""" """Class represent a hierarchical layout"""
@ -215,9 +215,13 @@ class VlsiLayout:
self.deduceHierarchy() self.deduceHierarchy()
# self.traverseTheHierarchy() # self.traverseTheHierarchy()
self.populateCoordinateMap() self.populateCoordinateMap()
#only ones with text
for layerNumber in self.layerNumbersInUse: for layerNumber in self.layerNumbersInUse:
self.processLabelPins((layerNumber, None)) #if layerNumber not in no_pin_shape:
if layerNumber in use_purpose:
self.processLabelPins((layerNumber, use_purpose[layerNumber]))
else:
self.processLabelPins((layerNumber, None))
def populateCoordinateMap(self): def populateCoordinateMap(self):
def addToXyTree(startingStructureName = None,transformPath = None): def addToXyTree(startingStructureName = None,transformPath = None):
@ -903,6 +907,16 @@ def sameLPP(lpp1, lpp2):
if lpp1[1] == None or lpp2[1] == None: if lpp1[1] == None or lpp2[1] == None:
return lpp1[0] == lpp2[0] return lpp1[0] == lpp2[0]
if isinstance(lpp1[1], list):
for i in range(len(lpp1[1])):
if lpp1[0] == lpp2[0] and lpp1[1][i] == lpp2[1]:
return True
if isinstance(lpp2[1], list):
for i in range(len(lpp2[1])):
if lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1][i]:
return True
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1] return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]

View File

@ -238,8 +238,8 @@ def setup_bitcell():
OPTS.dummy_bitcell = "dummy_pbitcell" OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.replica_bitcell = "replica_pbitcell" OPTS.replica_bitcell = "replica_pbitcell"
else: else:
num_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports OPTS.num_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
OPTS.bitcell = "bitcell_{}port".format(num_ports) OPTS.bitcell = "bitcell_{}port".format(OPTS.num_ports)
OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell
OPTS.replica_bitcell = "replica_" + OPTS.bitcell OPTS.replica_bitcell = "replica_" + OPTS.bitcell
@ -607,7 +607,7 @@ def report_status():
# If a write mask is specified by the user, the mask write size should be the same as # If a write mask is specified by the user, the mask write size should be the same as
# the word size so that an entire word is written at once. # the word size so that an entire word is written at once.
if OPTS.write_size is not None: if OPTS.write_size is not None and OPTS.write_size != OPTS.word_size:
if (OPTS.word_size % OPTS.write_size != 0): if (OPTS.word_size % OPTS.write_size != 0):
debug.error("Write size needs to be an integer multiple of word size.") debug.error("Write size needs to be an integer multiple of word size.")
# If write size is more than half of the word size, # If write size is more than half of the word size,

View File

@ -366,18 +366,13 @@ class bank(design.design):
# A space for wells or jogging m2 # A space for wells or jogging m2
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3 * self.m2_pitch) 3 * self.m2_pitch,
drc("nwell_to_nwell"))
def add_modules(self): def add_modules(self):
""" Add all the modules using the class loader """ """ Add all the modules using the class loader """
self.port_address = []
for port in self.all_ports:
self.port_address.append(factory.create(module_type="port_address",
cols=self.num_cols + self.num_spare_cols,
rows=self.num_rows,
port=port))
self.add_mod(self.port_address[port])
local_array_size = OPTS.local_array_size local_array_size = OPTS.local_array_size
@ -397,12 +392,26 @@ class bank(design.design):
cols=self.num_cols + self.num_spare_cols, cols=self.num_cols + self.num_spare_cols,
rows=self.num_rows) rows=self.num_rows)
self.add_mod(self.bitcell_array) self.add_mod(self.bitcell_array)
if self.num_spare_cols == 0:
self.num_spare_cols = (self.bitcell_array.column_size % (self.word_size *self.words_per_row))
self.port_address = []
for port in self.all_ports:
self.port_address.append(factory.create(module_type="port_address",
cols=self.bitcell_array.column_size,
rows=self.bitcell_array.row_size,
port=port))
self.add_mod(self.port_address[port])
self.port_data = [] self.port_data = []
self.bit_offsets = self.get_column_offsets() self.bit_offsets = self.get_column_offsets()
for port in self.all_ports: for port in self.all_ports:
temp_pre = factory.create(module_type="port_data", temp_pre = factory.create(module_type="port_data",
sram_config=self.sram_config, sram_config=self.sram_config,
dimension_override=True,
cols=self.bitcell_array.column_size - self.num_spare_cols,
rows=self.bitcell_array.row_size,
num_spare_cols=self.num_spare_cols,
port=port, port=port,
bit_offsets=self.bit_offsets) bit_offsets=self.bit_offsets)
self.port_data.append(temp_pre) self.port_data.append(temp_pre)
@ -430,7 +439,9 @@ class bank(design.design):
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins:
temp.append('vpb')
temp.append('vnb')
self.connect_inst(temp) self.connect_inst(temp)
def place_bitcell_array(self, offset): def place_bitcell_array(self, offset):
@ -489,7 +500,7 @@ class bank(design.design):
mod=self.port_address[port]) mod=self.port_address[port])
temp = [] temp = []
for bit in range(self.row_addr_size): for bit in range(ceil(log(self.bitcell_array.row_size, 2))):
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size)) temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
temp.append("wl_en{}".format(port)) temp.append("wl_en{}".format(port))
wordline_names = self.bitcell_array.get_wordline_names(port) wordline_names = self.bitcell_array.get_wordline_names(port)
@ -614,6 +625,10 @@ class bank(design.design):
self.copy_power_pins(inst, "vdd", add_vias=False) self.copy_power_pins(inst, "vdd", add_vias=False)
self.copy_power_pins(inst, "gnd", add_vias=False) self.copy_power_pins(inst, "gnd", add_vias=False)
if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins:
for pin_name, supply_name in zip(['vpb','vnb'],['vdd','gnd']):
self.copy_power_pins(self.bitcell_array_inst, pin_name, new_name=supply_name)
# If we use the pinvbuf as the decoder, we need to add power pins. # If we use the pinvbuf as the decoder, we need to add power pins.
# Other decoders already have them. # Other decoders already have them.
if self.col_addr_size == 1: if self.col_addr_size == 1:
@ -1062,7 +1077,6 @@ class bank(design.design):
to_layer="m2", to_layer="m2",
offset=control_pos) offset=control_pos)
def graph_exclude_precharge(self): def graph_exclude_precharge(self):
""" """
Precharge adds a loop between bitlines, can be excluded to reduce complexity Precharge adds a loop between bitlines, can be excluded to reduce complexity

View File

@ -109,7 +109,7 @@ class dff_buf(design.design):
except AttributeError: except AttributeError:
pass pass
well_spacing += self.well_extend_active well_spacing += 2 * self.well_extend_active
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing, 0)) self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing, 0))

View File

@ -12,6 +12,10 @@ 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 from tech import layer_properties as layer_props
from tech import layer_indices
from tech import layer_stacks
from tech import preferred_directions
from tech import drc
class hierarchical_predecode(design.design): class hierarchical_predecode(design.design):
@ -29,7 +33,7 @@ class hierarchical_predecode(design.design):
self.cell_height = height self.cell_height = height
self.column_decoder = column_decoder self.column_decoder = column_decoder
self.input_and_rail_pos = []
self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
super().__init__(name) super().__init__(name)
@ -183,9 +187,9 @@ class hierarchical_predecode(design.design):
def route(self): def route(self):
self.route_input_inverters() self.route_input_inverters()
self.route_output_inverters()
self.route_inputs_to_rails()
self.route_input_ands() self.route_input_ands()
self.route_output_inverters()
self.route_inputs_to_rails()
self.route_output_ands() self.route_output_ands()
self.route_vdd_gnd() self.route_vdd_gnd()
@ -274,8 +278,46 @@ class hierarchical_predecode(design.design):
# pins in the and gates. # pins in the and gates.
inv_out_pos = inv_out_pin.rc() inv_out_pos = inv_out_pin.rc()
y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0)
rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset) rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset)
# create via for dimensions
from_layer = self.output_layer
to_layer = self.bus_layer
cur_layer = from_layer
from_id = layer_indices[cur_layer]
to_id = layer_indices[to_layer]
if from_id < to_id: # grow the stack up
search_id = 0
next_id = 2
else: # grow the stack down
search_id = 2
next_id = 0
curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None)
via = factory.create(module_type="contact",
layer_stack=curr_stack,
dimensions=[1, 1],
directions=self.bus_directions)
overlapping_pin_space = drc["{0}_to_{0}".format(self.output_layer)]
total_buffer_space = (overlapping_pin_space + via.height)
#FIXME: compute rail locations instead of just guessing and nudging
while(True):
drc_error = 0
for and_input in self.input_and_rail_pos:
if and_input.x == rail_pos.x:
if (abs(y_offset - and_input.y) < total_buffer_space) and (abs(y_offset - and_input.y) > via.height):
drc_error = 1
if drc_error == 0:
break
else:
y_offset += drc["grid"]
rail_pos.y = y_offset
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0)
self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_stack_center(from_layer=inv_out_pin.layer, self.add_via_stack_center(from_layer=inv_out_pin.layer,
@ -316,6 +358,7 @@ class hierarchical_predecode(design.design):
to_layer=self.bus_layer, to_layer=self.bus_layer,
offset=rail_pos, offset=rail_pos,
directions=self.bus_directions) directions=self.bus_directions)
self.input_and_rail_pos.append(rail_pos)
if gate_pin == "A": if gate_pin == "A":
direction = None direction = None
else: else:

View File

@ -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 cell_properties
from tech import layer_properties as layer_props from tech import layer_properties as layer_props
@ -20,26 +21,36 @@ class port_data(design.design):
Port 0 always has the RBL on the left while port 1 is on the right. Port 0 always has the RBL on the left while port 1 is on the right.
""" """
def __init__(self, sram_config, port, bit_offsets=None, name=""): def __init__(self, sram_config, port, num_spare_cols=None, bit_offsets=None, name="", rows=None, cols=None, dimension_override=False):
sram_config.set_local_config(self) sram_config.set_local_config(self)
if dimension_override:
self.num_rows = rows
self.num_cols = cols
self.word_size = sram_config.word_size
self.port = port self.port = port
if self.write_size is not None: if self.write_size is not None:
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:
self.num_wmasks = 0 self.num_wmasks = 0
if self.num_spare_cols is None: if num_spare_cols:
self.num_spare_cols = num_spare_cols
elif self.num_spare_cols is None:
self.num_spare_cols = 0 self.num_spare_cols = 0
if not bit_offsets: if not bit_offsets:
bitcell = factory.create(module_type=OPTS.bitcell) bitcell = factory.create(module_type=OPTS.bitcell)
if(cell_properties.use_strap == True and OPTS.num_ports == 1):
strap = factory.create(module_type=cell_properties.strap_module, version=cell_properties.strap_version)
precharge_width = bitcell.width + strap.width
else:
precharge_width = bitcell.width
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 * precharge_width)
else: else:
self.bit_offsets = bit_offsets self.bit_offsets = bit_offsets
if name == "": if name == "":
name = "port_data_{0}".format(self.port) name = "port_data_{0}".format(self.port)
super().__init__(name) super().__init__(name)
@ -117,7 +128,6 @@ class port_data(design.design):
for bit in range(self.num_spare_cols): for bit in range(self.num_spare_cols):
self.add_pin("sparebl_{0}".format(bit), "INOUT") self.add_pin("sparebl_{0}".format(bit), "INOUT")
self.add_pin("sparebr_{0}".format(bit), "INOUT") self.add_pin("sparebr_{0}".format(bit), "INOUT")
if self.port in self.read_ports: if self.port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("dout_{}".format(bit), "OUTPUT") self.add_pin("dout_{}".format(bit), "OUTPUT")
@ -191,14 +201,19 @@ 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=OPTS.bitcell) cell = factory.create(module_type=OPTS.bitcell)
if(cell_properties.use_strap == True and OPTS.num_ports == 1):
strap = factory.create(module_type=cell_properties.strap_module, version=cell_properties.strap_version)
precharge_width = cell.width + strap.width
else:
precharge_width = cell.width
if self.port == 0: if self.port == 0:
# Append an offset on the left # Append an offset on the left
precharge_bit_offsets = [self.bit_offsets[0] - self.cell.width] + self.bit_offsets precharge_bit_offsets = [self.bit_offsets[0] - precharge_width] + self.bit_offsets
else: else:
# Append an offset on the right # Append an offset on the right
precharge_bit_offsets = self.bit_offsets + [self.bit_offsets[-1] + self.cell.width] precharge_bit_offsets = self.bit_offsets + [self.bit_offsets[-1] + precharge_width]
self.precharge_array = factory.create(module_type="precharge_array", self.precharge_array = factory.create(module_type="precharge_array",
columns=self.num_cols + self.num_spare_cols + 1, columns=self.num_cols + self.num_spare_cols + 1,
offsets=precharge_bit_offsets, offsets=precharge_bit_offsets,
@ -567,19 +582,32 @@ class port_data(design.design):
off = 1 off = 1
else: else:
off = 0 off = 0
if OPTS.num_ports > 1:
self.channel_route_bitlines(inst1=self.column_mux_array_inst,
inst1_bls_template="{inst}_out_{bit}",
inst2=inst2,
num_bits=self.word_size,
inst1_start_bit=start_bit)
self.channel_route_bitlines(inst1=self.column_mux_array_inst, self.channel_route_bitlines(inst1=self.precharge_array_inst,
inst1_bls_template="{inst}_out_{bit}", inst1_bls_template="{inst}_{bit}",
inst2=inst2, inst2=inst2,
num_bits=self.word_size, num_bits=self.num_spare_cols,
inst1_start_bit=start_bit) inst1_start_bit=self.num_cols + off,
inst2_start_bit=self.word_size)
else:
self.connect_bitlines(inst1=self.column_mux_array_inst,
inst1_bls_template="{inst}_out_{bit}",
inst2=inst2,
num_bits=self.word_size,
inst1_start_bit=start_bit)
self.channel_route_bitlines(inst1=self.precharge_array_inst, self.connect_bitlines(inst1=self.precharge_array_inst,
inst1_bls_template="{inst}_{bit}", inst1_bls_template="{inst}_{bit}",
inst2=inst2, inst2=inst2,
num_bits=self.num_spare_cols, num_bits=self.num_spare_cols,
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)
elif layer_props.port_data.channel_route_bitlines: elif layer_props.port_data.channel_route_bitlines:
self.channel_route_bitlines(inst1=inst1, self.channel_route_bitlines(inst1=inst1,

View File

@ -76,8 +76,8 @@ class precharge_array(design.design):
size=self.size, size=self.size,
bitcell_bl=self.bitcell_bl, bitcell_bl=self.bitcell_bl,
bitcell_br=self.bitcell_br) bitcell_br=self.bitcell_br)
self.add_mod(self.pc_cell) self.add_mod(self.pc_cell)
self.cell = factory.create(module_type=OPTS.bitcell) self.cell = factory.create(module_type=OPTS.bitcell)
def add_layout_pins(self): def add_layout_pins(self):

View File

@ -6,7 +6,7 @@
import debug import debug
from bitcell_base_array import bitcell_base_array from bitcell_base_array import bitcell_base_array
from tech import drc, spice from tech import drc, spice, cell_properties
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

View File

@ -8,6 +8,7 @@
import design import design
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
from tech import cell_properties
import debug import debug
from globals import OPTS from globals import OPTS
@ -41,7 +42,6 @@ class sense_amp_array(design.design):
self.en_layer = "m3" self.en_layer = "m3"
else: else:
self.en_layer = "m1" self.en_layer = "m1"
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -109,15 +109,22 @@ class sense_amp_array(design.design):
self.en_name, "vdd", "gnd"]) self.en_name, "vdd", "gnd"])
def place_sense_amp_array(self): def place_sense_amp_array(self):
if self.bitcell.width > self.amp.width: cell = factory.create(module_type=OPTS.bitcell)
self.amp_spacing = self.bitcell.width if(cell_properties.use_strap == True and OPTS.num_ports == 1):
strap = factory.create(module_type=cell_properties.strap_module, version=cell_properties.strap_version)
precharge_width = cell.width + strap.width
else:
precharge_width = cell.width
if precharge_width > self.amp.width:
self.amp_spacing = precharge_width
else: else:
self.amp_spacing = self.amp.width self.amp_spacing = self.amp.width
if not self.offsets: if not self.offsets:
self.offsets = [] self.offsets = []
for i in range(self.num_cols + self.num_spare_cols): for i in range(self.num_cols + self.num_spare_cols):
self.offsets.append(i * self.bitcell.width) self.offsets.append(i * self.amp_spacing)
for i, xoffset in enumerate(self.offsets[0:self.num_cols:self.words_per_row]): for i, xoffset in enumerate(self.offsets[0:self.num_cols:self.words_per_row]):
if self.bitcell.mirror.y and (i * self.words_per_row + self.column_offset) % 2: if self.bitcell.mirror.y and (i * self.words_per_row + self.column_offset) % 2:
@ -128,13 +135,12 @@ class sense_amp_array(design.design):
amp_position = vector(xoffset, 0) amp_position = vector(xoffset, 0)
self.local_insts[i].place(offset=amp_position, mirror=mirror) self.local_insts[i].place(offset=amp_position, mirror=mirror)
# place spare sense amps (will share the same enable as regular sense amps) # place spare sense amps (will share the same enable as regular sense amps)
for i, xoffset in enumerate(self.offsets[self.num_cols:]): for i, xoffset in enumerate(self.offsets[self.num_cols:]):
index = self.word_size + i index = self.word_size + i
if self.bitcell.mirror.y and (index + self.column_offset) % 2: if self.bitcell.mirror.y and (index + self.column_offset) % 2:
mirror = "MY" mirror = "MY"
xoffset = xoffset + self.amp_width xoffset = xoffset + self.amp_spacing
else: else:
mirror = "" mirror = ""

View File

@ -72,7 +72,9 @@ class options(optparse.Values):
# This is the temp directory where all intermediate results are stored. # This is the temp directory where all intermediate results are stored.
try: try:
# If user defined the temporary location in their environment, use it # If user defined the temporary location in their environment, use it
openram_temp = os.path.abspath(os.environ.get("OPENRAM_TMP")) openram_temp = os.path.abspath(os.environ.get("OPENRAM_TMP"))
except: except:
openram_temp = "/tmp" openram_temp = "/tmp"
@ -99,6 +101,7 @@ class options(optparse.Values):
netlist_only = False netlist_only = False
# Whether we should do the final power routing # Whether we should do the final power routing
route_supplies = "tree" route_supplies = "tree"
supply_pin_type = "ring"
# This determines whether LVS and DRC is checked at all. # This determines whether LVS and DRC is checked at all.
check_lvsdrc = False check_lvsdrc = False
# This determines whether LVS and DRC is checked for every submodule. # This determines whether LVS and DRC is checked for every submodule.
@ -119,13 +122,13 @@ class options(optparse.Values):
# Tool options # Tool options
################### ###################
# Variable to select the variant of spice # Variable to select the variant of spice
spice_name = "" spice_name = None
# The spice executable being used which is derived from the user PATH. # The spice executable being used which is derived from the user PATH.
spice_exe = "" spice_exe = None
# Variable to select the variant of drc, lvs, pex # Variable to select the variant of drc, lvs, pex
drc_name = "" drc_name = None
lvs_name = "" lvs_name = None
pex_name = "" pex_name = None
# The DRC/LVS/PEX executable being used # The DRC/LVS/PEX executable being used
# which is derived from the user PATH. # which is derived from the user PATH.
drc_exe = None drc_exe = None

View File

@ -56,7 +56,13 @@ class column_mux(pgate.pgate):
self.place_ptx() self.place_ptx()
self.width = self.bitcell.width cell = factory.create(module_type=OPTS.bitcell)
if(cell_props.use_strap == True and OPTS.num_ports == 1):
strap = factory.create(module_type=cell_props.strap_module, version=cell_props.strap_version)
precharge_width = cell.width + strap.width
else:
precharge_width = cell.width
self.width = precharge_width
self.height = self.nmos_upper.uy() + self.pin_height self.height = self.nmos_upper.uy() + self.pin_height
self.connect_poly() self.connect_poly()
@ -217,10 +223,15 @@ class column_mux(pgate.pgate):
Add a well and implant over the whole cell. Also, add the Add a well and implant over the whole cell. Also, add the
pwell contact (if it exists) pwell contact (if it exists)
""" """
if(cell_props.use_strap == True and OPTS.num_ports == 1):
strap = factory.create(module_type=cell_props.strap_module, version=cell_props.strap_version)
rbc_width = self.bitcell.width + strap.width
else:
rbc_width = self.bitcell.width
# Add it to the right, aligned in between the two tx # Add it to the right, aligned in between the two tx
active_pos = vector(self.bitcell.width, active_pos = vector(rbc_width,
self.nmos_upper.by() - 0.5 * self.poly_space) self.nmos_upper.by() - 0.5 * self.poly_space)
self.add_via_center(layers=self.active_stack, self.add_via_center(layers=self.active_stack,
offset=active_pos, offset=active_pos,
implant_type="p", implant_type="p",
@ -239,5 +250,5 @@ class column_mux(pgate.pgate):
if "pwell" in layer: if "pwell" in layer:
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=vector(0, 0), offset=vector(0, 0),
width=self.bitcell.width, width=rbc_width,
height=self.height) height=self.height)

View File

@ -30,7 +30,11 @@ class precharge(design.design):
self.beta = parameter["beta"] self.beta = parameter["beta"]
self.ptx_width = self.beta * parameter["min_tx_size"] self.ptx_width = self.beta * parameter["min_tx_size"]
self.ptx_mults = 1 self.ptx_mults = 1
self.width = self.bitcell.width if(cell_props.use_strap == True and OPTS.num_ports == 1):
strap = factory.create(module_type=cell_props.strap_module, version=cell_props.strap_version)
self.width = self.bitcell.width + strap.width
else:
self.width = self.bitcell.width
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br
self.bitcell_bl_pin =self.bitcell.get_pin(self.bitcell_bl) self.bitcell_bl_pin =self.bitcell.get_pin(self.bitcell_bl)

View File

@ -37,6 +37,8 @@ class grid:
# This is really lower left bottom layer and upper right top layer in 3D. # This is really lower left bottom layer and upper right top layer in 3D.
self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round() self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round()
self.ur = vector3d(ur.x, ur.y, 0).scale(self.track_factor).round() self.ur = vector3d(ur.x, ur.y, 0).scale(self.track_factor).round()
debug.info(1, "BBOX coords: ll=" + str(ll) + " ur=" + str(ur))
debug.info(1, "BBOX grids: ll=" + str(self.ll) + " ur=" + str(self.ur))
# let's leave the map sparse, cells are created on demand to reduce memory # let's leave the map sparse, cells are created on demand to reduce memory
self.map={} self.map={}
@ -127,33 +129,47 @@ class grid:
Side specifies which side. Side specifies which side.
Layer specifies horizontal (0) or vertical (1) Layer specifies horizontal (0) or vertical (1)
Width specifies how wide the perimter "stripe" should be. Width specifies how wide the perimter "stripe" should be.
Works from the inside out from the bbox (ll, ur)
""" """
if "ring" in side:
ring_width = width
else:
ring_width = 0
if "ring" in side:
ring_offset = offset
else:
ring_offset = 0
perimeter_list = [] perimeter_list = []
# Add the left/right columns # Add the left/right columns
if side=="all" or side=="left": if side=="all" or "left" in side:
for x in range(self.ll.x + offset, self.ll.x + width + offset, 1): for x in range(self.ll.x - offset, self.ll.x - width - offset, -1):
for y in range(self.ll.y + offset + margin, self.ur.y - offset - margin, 1): for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
for layer in layers: for layer in layers:
perimeter_list.append(vector3d(x, y, layer)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="right": if side=="all" or "right" in side:
for x in range(self.ur.x - width - offset, self.ur.x - offset, 1): for x in range(self.ur.x + offset, self.ur.x + width + offset, 1):
for y in range(self.ll.y + offset + margin, self.ur.y - offset - margin, 1): for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
for layer in layers: for layer in layers:
perimeter_list.append(vector3d(x, y, layer)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="bottom": if side=="all" or "bottom" in side:
for y in range(self.ll.y + offset, self.ll.y + width + offset, 1): for y in range(self.ll.y - offset, self.ll.y - width - offset, -1):
for x in range(self.ll.x + offset + margin, self.ur.x - offset - margin, 1): for x in range(self.ll.x - ring_offset - margin - ring_width + 1, self.ur.x + ring_offset + margin + ring_width, 1):
for layer in layers: for layer in layers:
perimeter_list.append(vector3d(x, y, layer)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="top": if side=="all" or "top" in side:
for y in range(self.ur.y - width - offset, self.ur.y - offset, 1): for y in range(self.ur.y + offset, self.ur.y + width + offset, 1):
for x in range(self.ll.x + offset + margin, self.ur.x - offset - margin, 1): for x in range(self.ll.x - ring_offset - margin - ring_width + 1, self.ur.x + ring_offset + margin + ring_width, 1):
for layer in layers: for layer in layers:
perimeter_list.append(vector3d(x, y, layer)) perimeter_list.append(vector3d(x, y, layer))
# Add them all to the map
self.add_map(perimeter_list)
return perimeter_list return perimeter_list
def add_perimeter_target(self, side="all", layers=[0, 1]): def add_perimeter_target(self, side="all", layers=[0, 1]):

View File

@ -82,31 +82,13 @@ class router(router_tech):
""" """
Initialize the ll,ur values with the paramter or using the layout boundary. Initialize the ll,ur values with the paramter or using the layout boundary.
""" """
# If didn't specify a gds blockage file, write it out to read the gds
# This isn't efficient, but easy for now
# Load the gds file and read in all the shapes
self.cell.gds_write(self.gds_filename)
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(self.gds_filename)
self.top_name = self.layout.rootStructureName
if not bbox: if not bbox:
# The boundary will determine the limits to the size self.bbox = self.cell.get_bbox(margin)
# of the routing grid
self.boundary = self.layout.measureBoundary(self.top_name)
# These must be un-indexed to get rid of the matrix type
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
else: else:
self.ll, self.ur = bbox self.bbox = bbox
(self.ll, self.ur) = self.bbox
margin_offset = vector(margin, margin)
self.bbox = (self.ll - margin_offset, self.ur + margin_offset)
size = self.ur - self.ll
debug.info(1, "Size: {0} x {1} with perimeter margin {2}".format(size.x, size.y, margin))
def get_bbox(self): def get_bbox(self):
return self.bbox return self.bbox
@ -893,19 +875,21 @@ class router(router_tech):
# Clearing the blockage of this pin requires the inflated pins # Clearing the blockage of this pin requires the inflated pins
self.clear_blockages(pin_name) self.clear_blockages(pin_name)
def add_side_supply_pin(self, name, side="left", width=2): def add_side_supply_pin(self, name, side="left", width=3, space=2):
""" """
Adds a supply pin to the perimeter and resizes the bounding box. Adds a supply pin to the perimeter and resizes the bounding box.
""" """
pg = pin_group(name, [], self) pg = pin_group(name, [], self)
# Offset two spaces inside and one between the rings
if name == "gnd": if name == "gnd":
offset = width + 1 offset = width + 2 * space
else: else:
offset = 1 offset = space
if side in ["left", "right"]: if side in ["left", "right"]:
layers = [1] layers = [1]
else: else:
layers = [0] layers = [0]
pg.grids = set(self.rg.get_perimeter_list(side=side, pg.grids = set(self.rg.get_perimeter_list(side=side,
width=width, width=width,
margin=self.margin, margin=self.margin,
@ -920,39 +904,39 @@ class router(router_tech):
self.new_pins[name] = pg.pins self.new_pins[name] = pg.pins
def add_ring_supply_pin(self, name, width=2): def add_ring_supply_pin(self, name, width=3, space=2):
""" """
Adds a ring supply pin that goes inside the given bbox. Adds a ring supply pin that goes inside the given bbox.
""" """
pg = pin_group(name, [], self) pg = pin_group(name, [], self)
# Offset the vdd inside one ring width # Offset two spaces inside and one between the rings
# Units are in routing grids # Units are in routing grids
if name == "gnd": if name == "gnd":
offset = width + 1 offset = width + 2 * space
else: else:
offset = 1 offset = space
# LEFT # LEFT
left_grids = set(self.rg.get_perimeter_list(side="left", left_grids = set(self.rg.get_perimeter_list(side="left_ring",
width=width, width=width,
margin=self.margin, margin=self.margin,
offset=offset, offset=offset,
layers=[1])) layers=[1]))
# RIGHT # RIGHT
right_grids = set(self.rg.get_perimeter_list(side="right", right_grids = set(self.rg.get_perimeter_list(side="right_ring",
width=width, width=width,
margin=self.margin, margin=self.margin,
offset=offset, offset=offset,
layers=[1])) layers=[1]))
# TOP # TOP
top_grids = set(self.rg.get_perimeter_list(side="top", top_grids = set(self.rg.get_perimeter_list(side="top_ring",
width=width, width=width,
margin=self.margin, margin=self.margin,
offset=offset, offset=offset,
layers=[0])) layers=[0]))
# BOTTOM # BOTTOM
bottom_grids = set(self.rg.get_perimeter_list(side="bottom", bottom_grids = set(self.rg.get_perimeter_list(side="bottom_ring",
width=width, width=width,
margin=self.margin, margin=self.margin,
offset=offset, offset=offset,

View File

@ -21,7 +21,7 @@ class supply_grid_router(router):
routes a grid to connect the supply on the two layers. routes a grid to connect the supply on the two layers.
""" """
def __init__(self, layers, design, margin=0, bbox=None): def __init__(self, layers, design, bbox=None, pin_type=None):
""" """
This will route on layers in design. It will get the blockages from This will route on layers in design. It will get the blockages from
either the gds file name or the design itself (by saving to a gds file). either the gds file name or the design itself (by saving to a gds file).

View File

@ -34,7 +34,7 @@ class supply_tree_router(router):
# The pin escape router already made the bounding box big enough, # The pin escape router already made the bounding box big enough,
# so we can use the regular bbox here. # so we can use the regular bbox here.
if pin_type: if pin_type:
debug.check(pin_type in ["left", "right", "top", "bottom", "tree", "ring"], debug.check(pin_type in ["left", "right", "top", "bottom", "single", "ring"],
"Invalid pin type {}".format(pin_type)) "Invalid pin type {}".format(pin_type))
self.pin_type = pin_type self.pin_type = pin_type
router.__init__(self, router.__init__(self,

View File

@ -9,6 +9,7 @@ from vector import vector
from sram_base import sram_base from sram_base import sram_base
from contact import m2_via from contact import m2_via
from channel_route import channel_route from channel_route import channel_route
from router_tech import router_tech
from globals import OPTS from globals import OPTS
@ -329,13 +330,31 @@ class sram_1bank(sram_base):
# Some technologies have an isolation # Some technologies have an isolation
self.add_dnwell(inflate=2) self.add_dnwell(inflate=2)
# We need the initial bbox for the supply rings later
# because the perimeter pins will change the bbox
# Route the pins to the perimeter # Route the pins to the perimeter
pre_bbox = None
if OPTS.perimeter_pins: if OPTS.perimeter_pins:
self.route_escape_pins() rt = router_tech(self.supply_stack, 1)
if OPTS.supply_pin_type in ["ring", "left", "right", "top", "bottom"]:
big_margin = 12 * rt.track_width
little_margin = 2 * rt.track_width
else:
big_margin = 6 * rt.track_width
little_margin = 0
pre_bbox = self.get_bbox(side="ring",
big_margin=rt.track_width)
bbox = self.get_bbox(side=OPTS.supply_pin_type,
big_margin=big_margin,
little_margin=little_margin)
self.route_escape_pins(bbox)
# Route the supplies first since the MST is not blockage aware # Route the supplies first since the MST is not blockage aware
# and signals can route to anywhere on sides (it is flexible) # and signals can route to anywhere on sides (it is flexible)
self.route_supplies() self.route_supplies(pre_bbox)
def route_dffs(self, add_routes=True): def route_dffs(self, add_routes=True):
@ -366,6 +385,7 @@ class sram_1bank(sram_base):
if len(route_map) > 0: if len(route_map) > 0:
# This layer stack must be different than the data dff layer stack
layer_stack = self.m1_stack layer_stack = self.m1_stack
if port == 0: if port == 0:
@ -375,11 +395,11 @@ class sram_1bank(sram_base):
offset=offset, offset=offset,
layer_stack=layer_stack, layer_stack=layer_stack,
parent=self) parent=self)
# This causes problem in magic since it sometimes cannot extract connectivity of isntances # This causes problem in magic since it sometimes cannot extract connectivity of instances
# with no active devices. # with no active devices.
self.add_inst(cr.name, cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
#self.add_flat_inst(cr.name, cr) # self.add_flat_inst(cr.name, cr)
else: else:
offset = vector(0, offset = vector(0,
self.bank.height + self.m3_pitch) self.bank.height + self.m3_pitch)
@ -387,11 +407,11 @@ class sram_1bank(sram_base):
offset=offset, offset=offset,
layer_stack=layer_stack, layer_stack=layer_stack,
parent=self) parent=self)
# This causes problem in magic since it sometimes cannot extract connectivity of isntances # This causes problem in magic since it sometimes cannot extract connectivity of instances
# with no active devices. # with no active devices.
self.add_inst(cr.name, cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
#self.add_flat_inst(cr.name, cr) # self.add_flat_inst(cr.name, cr)
def route_data_dffs(self, port, add_routes): def route_data_dffs(self, port, add_routes):
route_map = [] route_map = []
@ -422,40 +442,49 @@ class sram_1bank(sram_base):
if len(route_map) > 0: if len(route_map) > 0:
# The write masks will have blockages on M1 # This layer stack must be different than the column addr dff layer stack
# if self.num_wmasks > 0 and port in self.write_ports:
# layer_stack = self.m3_stack
# else:
# layer_stack = self.m1_stack
layer_stack = self.m3_stack layer_stack = self.m3_stack
if port == 0: if port == 0:
# This is relative to the bank at 0,0 or the s_en which is routed on M3 also
if "s_en" in self.control_logic_insts[port].mod.pin_map:
y_bottom = min(0, self.control_logic_insts[port].get_pin("s_en").by())
else:
y_bottom = 0
y_offset = y_bottom - self.data_bus_size[port] + 2 * self.m3_pitch
offset = vector(self.control_logic_insts[port].rx() + self.dff.width, offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m3_pitch) y_offset)
cr = channel_route(netlist=route_map, cr = channel_route(netlist=route_map,
offset=offset, offset=offset,
layer_stack=layer_stack, layer_stack=layer_stack,
parent=self) parent=self)
if add_routes: if add_routes:
# This causes problem in magic since it sometimes cannot extract connectivity of isntances # This causes problem in magic since it sometimes cannot extract connectivity of instances
# with no active devices. # with no active devices.
self.add_inst(cr.name, cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
#self.add_flat_inst(cr.name, cr) # self.add_flat_inst(cr.name, cr)
else: else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
else: else:
if "s_en" in self.control_logic_insts[port].mod.pin_map:
y_top = max(self.bank.height, self.control_logic_insts[port].get_pin("s_en").uy())
else:
y_top = self.bank.height
y_offset = y_top + self.m3_pitch
offset = vector(0, offset = vector(0,
self.bank.height + self.m3_pitch) y_offset)
cr = channel_route(netlist=route_map, cr = channel_route(netlist=route_map,
offset=offset, offset=offset,
layer_stack=layer_stack, layer_stack=layer_stack,
parent=self) parent=self)
if add_routes: if add_routes:
# This causes problem in magic since it sometimes cannot extract connectivity of isntances # This causes problem in magic since it sometimes cannot extract connectivity of instances
# with no active devices. # with no active devices.
self.add_inst(cr.name, cr) self.add_inst(cr.name, cr)
self.connect_inst([]) self.connect_inst([])
#self.add_flat_inst(cr.name, cr) # self.add_flat_inst(cr.name, cr)
else: else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap

View File

@ -41,6 +41,14 @@ class sram_base(design, verilog, lef):
if not self.num_spare_cols: if not self.num_spare_cols:
self.num_spare_cols = 0 self.num_spare_cols = 0
try:
from tech import power_grid
self.supply_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
# Route a M3/M4 grid
self.supply_stack = self.m3_stack
def add_pins(self): def add_pins(self):
""" Add pins for entire SRAM. """ """ Add pins for entire SRAM. """
@ -230,7 +238,7 @@ class sram_base(design, verilog, lef):
def create_modules(self): def create_modules(self):
debug.error("Must override pure virtual function.", -1) debug.error("Must override pure virtual function.", -1)
def route_supplies(self): def route_supplies(self, bbox=None):
""" Route the supply grid and connect the pins to them. """ """ Route the supply grid and connect the pins to them. """
# Copy the pins to the top level # Copy the pins to the top level
@ -239,29 +247,21 @@ class sram_base(design, verilog, lef):
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name]) self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name])
try:
from tech import power_grid
grid_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
# Route a M3/M4 grid
grid_stack = self.m3_stack
if not OPTS.route_supplies: if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins) # Do not route the power supply (leave as must-connect pins)
return return
elif OPTS.route_supplies == "grid": elif OPTS.route_supplies == "grid":
from supply_grid_router import supply_grid_router as router from supply_grid_router import supply_grid_router as router
rtr=router(grid_stack, self)
else: else:
from supply_tree_router import supply_tree_router as router from supply_tree_router import supply_tree_router as router
rtr=router(grid_stack, rtr=router(layers=self.supply_stack,
self, design=self,
pin_type=OPTS.route_supplies) bbox=bbox,
pin_type=OPTS.supply_pin_type)
rtr.route() rtr.route()
if OPTS.route_supplies in ["left", "right", "top", "bottom", "ring"]: if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]:
# Find the lowest leftest pin for vdd and gnd # Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles # Copy the pin shape(s) to rectangles
@ -283,7 +283,7 @@ class sram_base(design, verilog, lef):
pin.width(), pin.width(),
pin.height()) pin.height())
elif OPTS.route_supplies: elif OPTS.route_supplies and OPTS.supply_pin_type == "single":
# Update these as we may have routed outside the region (perimeter pins) # Update these as we may have routed outside the region (perimeter pins)
lowest_coord = self.find_lowest_coords() lowest_coord = self.find_lowest_coords()
@ -321,7 +321,7 @@ class sram_base(design, verilog, lef):
# Grid is left with many top level pins # Grid is left with many top level pins
pass pass
def route_escape_pins(self): def route_escape_pins(self, bbox):
""" """
Add the top-level pins for a single bank SRAM with control. Add the top-level pins for a single bank SRAM with control.
""" """
@ -364,7 +364,7 @@ class sram_base(design, verilog, lef):
from signal_escape_router import signal_escape_router as router from signal_escape_router import signal_escape_router as router
rtr=router(layers=self.m3_stack, rtr=router(layers=self.m3_stack,
design=self, design=self,
margin=8 * self.m3_pitch) bbox=bbox)
rtr.escape_route(pins_to_route) rtr.escape_route(pins_to_route)
def compute_bus_sizes(self): def compute_bus_sizes(self):
@ -472,6 +472,12 @@ class sram_base(design, verilog, lef):
self.bitcell = factory.create(module_type=OPTS.bitcell) self.bitcell = factory.create(module_type=OPTS.bitcell)
self.dff = factory.create(module_type="dff") self.dff = factory.create(module_type="dff")
# Create the bank module (up to four are instantiated)
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
self.add_mod(self.bank)
self.num_spare_cols = self.bank.num_spare_cols
# Create the address and control flops (but not the clk) # Create the address and control flops (but not the clk)
self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1) self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1)
self.add_mod(self.row_addr_dff) self.add_mod(self.row_addr_dff)
@ -493,10 +499,6 @@ class sram_base(design, verilog, lef):
self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols) self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols)
self.add_mod(self.spare_wen_dff) self.add_mod(self.spare_wen_dff)
# Create the bank module (up to four are instantiated)
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
self.add_mod(self.bank)
# Create bank decoder # Create bank decoder
if(self.num_banks > 1): if(self.num_banks > 1):
self.add_multi_bank_modules() self.add_multi_bank_modules()

View File

@ -45,8 +45,8 @@ class timing_setup_test(openram_test):
'setup_times_HL': [0.16357419999999998], 'setup_times_HL': [0.16357419999999998],
'setup_times_LH': [0.1757812]} 'setup_times_LH': [0.1757812]}
elif OPTS.tech_name == "sky130": elif OPTS.tech_name == "sky130":
golden_data = {'hold_times_HL': [-0.05615234], golden_data = {'hold_times_HL': [-0.03173828],
'hold_times_LH': [-0.03173828], 'hold_times_LH': [-0.05615234],
'setup_times_HL': [0.078125], 'setup_times_HL': [0.078125],
'setup_times_LH': [0.1025391]} 'setup_times_LH': [0.1025391]}
else: else:

View File

@ -45,8 +45,8 @@ class timing_setup_test(openram_test):
'setup_times_HL': [0.1757812], 'setup_times_HL': [0.1757812],
'setup_times_LH': [0.1879883]} 'setup_times_LH': [0.1879883]}
elif OPTS.tech_name == "sky130": elif OPTS.tech_name == "sky130":
golden_data = {'hold_times_HL': [-0.05615234], golden_data = {'hold_times_HL': [-0.03173828],
'hold_times_LH': [-0.03173828], 'hold_times_LH': [-0.05615234],
'setup_times_HL': [0.078125], 'setup_times_HL': [0.078125],
'setup_times_LH': [0.1025391]} 'setup_times_LH': [0.1025391]}
else: else:

View File

@ -45,8 +45,8 @@ class timing_setup_test(openram_test):
'setup_times_HL': [0.16357419999999998], 'setup_times_HL': [0.16357419999999998],
'setup_times_LH': [0.1757812]} 'setup_times_LH': [0.1757812]}
elif OPTS.tech_name == "sky130": elif OPTS.tech_name == "sky130":
golden_data = {'hold_times_HL': [-0.05615234], golden_data = {'hold_times_HL': [-0.03173828],
'hold_times_LH': [-0.03173828], 'hold_times_LH': [-0.05615234],
'setup_times_HL': [0.078125], 'setup_times_HL': [0.078125],
'setup_times_LH': [0.1025391]} 'setup_times_LH': [0.1025391]}
else: else:

View File

@ -89,7 +89,10 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1])) f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1]))
# Do not run DRC for extraction/conversion # Do not run DRC for extraction/conversion
f.write("drc off\n") f.write("drc off\n")
f.write("gds polygon subcell true\n") f.write("set VDD vdd\n")
f.write("set GND gnd\n")
f.write("set SUB gnd\n")
#f.write("gds polygon subcell true\n")
f.write("gds warning default\n") f.write("gds warning default\n")
# These two options are temporarily disabled until Tim fixes a bug in magic related # These two options are temporarily disabled until Tim fixes a bug in magic related
# to flattening channel routes and vias (hierarchy with no devices in it). Otherwise, # to flattening channel routes and vias (hierarchy with no devices in it). Otherwise,
@ -177,6 +180,10 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.write('puts "Finished drc check"\n') f.write('puts "Finished drc check"\n')
f.write("drc catchup\n") f.write("drc catchup\n")
f.write('puts "Finished drc catchup"\n') f.write('puts "Finished drc catchup"\n')
# This is needed instead of drc count total because it displays
# some errors that are not "DRC" errors.
# f.write("puts -nonewline \"Total DRC errors found: \"\n")
# f.write("puts stdout [drc listall count total]\n")
f.write("drc count total\n") f.write("drc count total\n")
f.write("quit -noprompt\n") f.write("quit -noprompt\n")
f.write("EOF\n") f.write("EOF\n")
@ -244,11 +251,14 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out
if not output_path: if not output_path:
output_path = OPTS.openram_temp output_path = OPTS.openram_temp
setup_file = "setup.tcl" # Copy .magicrc file into the output directory
full_setup_file = OPTS.openram_tech + "tech/" + setup_file setup_file = os.environ.get('OPENRAM_NETGENRC', None)
if os.path.exists(full_setup_file): if not setup_file:
setup_file = OPTS.openram_tech + "tech/setup.tcl"
if os.path.exists(setup_file):
# Copy setup.tcl file into temp dir # Copy setup.tcl file into temp dir
shutil.copy(full_setup_file, output_path) shutil.copy(setup_file, output_path)
else: else:
setup_file = 'nosetup' setup_file = 'nosetup'

View File

@ -135,6 +135,8 @@ layer["m10"] = (29, 0)
layer["text"] = (239, 0) layer["text"] = (239, 0)
layer["boundary"]= (239, 0) layer["boundary"]= (239, 0)
use_purpose = {}
# Layer names for external PDKs # Layer names for external PDKs
layer_names = {} layer_names = {}
layer_names["active"] = "active" layer_names["active"] = "active"

View File

@ -63,6 +63,7 @@ layer["text"] = (63, 0)
layer["boundary"] = (63, 0) layer["boundary"] = (63, 0)
layer["blockage"] = (83, 0) layer["blockage"] = (83, 0)
use_purpose = {}
################################################### ###################################################
##END GDS Layer Map ##END GDS Layer Map
################################################### ###################################################

View File

@ -119,6 +119,8 @@ layer["m4"] = (31, 0)
layer["text"] = (63, 0) layer["text"] = (63, 0)
layer["boundary"] = (63, 0) layer["boundary"] = (63, 0)
use_purpose = {}
# Layer names for external PDKs # Layer names for external PDKs
layer_names = {} layer_names = {}
layer_names["active"] = "active" layer_names["active"] = "active"