Merge branch 'dev' of github:VLSIDA/OpenRAM into CalibrePexFilesUpdate

This commit is contained in:
Bob Vanhoof 2020-08-03 09:32:27 +02:00
commit 487bb6c6e9
21 changed files with 484 additions and 305 deletions

View File

@ -12,6 +12,69 @@ from vector import vector
import design import design
class channel_net():
def __init__(self, net_name, pins, vertical):
self.name = net_name
self.pins = pins
self.vertical = vertical
# Keep track of the internval
if vertical:
self.min_value = min(i.by() for i in pins)
self.max_value = max(i.uy() for i in pins)
else:
self.min_value = min(i.lx() for i in pins)
self.max_value = max(i.rx() for i in pins)
# Keep track of the conflicts
self.conflicts = []
def __str__(self):
return self.name
def __repr__(self):
return self.name
def __lt__(self, other):
return self.min_value < other.min_value
def pin_overlap(self, pin1, pin2, pitch):
""" Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin,
# for example, so the extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
# Pin 1 must be in the "LEFT" set
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap)
return overlaps
def pins_overlap(self, other, pitch):
"""
Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps.
"""
for pin1 in self.pins:
for pin2 in other.pins:
if self.pin_overlap(pin1, pin2, pitch):
return True
return False
def segment_overlap(self, other):
"""
Check if the horizontal span of the two nets overlaps eachother.
"""
min_overlap = self.min_value >= other.min_value and self.min_value <= other.max_value
max_overlap = self.max_value >= other.min_value and self.max_value <= other.max_value
return min_overlap or max_overlap
class channel_route(design.design): class channel_route(design.design):
unique_id = 0 unique_id = 0
@ -21,7 +84,8 @@ class channel_route(design.design):
offset, offset,
layer_stack, layer_stack,
directions=None, directions=None,
vertical=False): vertical=False,
parent=None):
""" """
The net list is a list of the nets with each net being a list of pins The net list is a list of the nets with each net being a list of pins
to be connected. The offset is the lower-left of where the to be connected. The offset is the lower-left of where the
@ -40,6 +104,8 @@ class channel_route(design.design):
self.layer_stack = layer_stack self.layer_stack = layer_stack
self.directions = directions self.directions = directions
self.vertical = vertical self.vertical = vertical
# For debugging...
self.parent = parent
if not directions or directions == "pref": if not directions or directions == "pref":
# Use the preferred layer directions # Use the preferred layer directions
@ -86,114 +152,139 @@ class channel_route(design.design):
# FIXME: This is O(n^2), so maybe optimize it. # FIXME: This is O(n^2), so maybe optimize it.
for other_pin, conflicts in g.items(): for other_pin, conflicts in g.items():
if pin in conflicts: if pin in conflicts:
conflicts.remove(pin) g[other_pin].remove(pin)
g[other_pin]=conflicts
return g return g
def route(self):
# Create names for the nets for the graphs
nets = []
index = 0
# print(self.netlist)
for pin_list in self.netlist:
nets.append(channel_net("n{}".format(index), pin_list, self.vertical))
index += 1
def vcg_nets_overlap(self, net1, net2): # Create the (undirected) horizontal constraint graph
""" hcg = collections.OrderedDict()
Check all the pin pairs on two nets and return a pin for net1 in nets:
overlap if any pin overlaps. for net2 in nets:
""" if net1.name == net2.name:
continue
if net1.segment_overlap(net2):
try:
hcg[net1.name].add(net2.name)
except KeyError:
hcg[net1.name] = set([net2.name])
try:
hcg[net2.name].add(net1.name)
except KeyError:
hcg[net2.name] = set([net1.name])
# Initialize the vertical conflict graph (vcg)
# and make a list of all pins
vcg = collections.OrderedDict()
# print("Nets:")
# for net_name in nets:
# print(net_name, [x.name for x in nets[net_name]])
# Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now
if self.vertical: if self.vertical:
pitch = self.horizontal_nonpref_pitch pitch = self.horizontal_nonpref_pitch
else: else:
pitch = self.vertical_nonpref_pitch pitch = self.vertical_nonpref_pitch
for pin1 in net1: for net in nets:
for pin2 in net2: vcg[net.name] = set()
if self.vcg_pin_overlap(pin1, pin2, pitch):
return True
return False for net1 in nets:
for net2 in nets:
def route(self):
# FIXME: Must extend this to a horizontal conflict graph
# too if we want to minimize the
# number of tracks!
# hcg = {}
# Initialize the vertical conflict graph (vcg)
# and make a list of all pins
vcg = collections.OrderedDict()
# Create names for the nets for the graphs
nets = collections.OrderedDict()
index = 0
# print(netlist)
for pin_list in self.netlist:
net_name = "n{}".format(index)
index += 1
nets[net_name] = pin_list
# print("Nets:")
# for net_name in nets:
# print(net_name, [x.name for x in nets[net_name]])
# Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now
for net_name1 in nets:
if net_name1 not in vcg.keys():
vcg[net_name1] = []
for net_name2 in nets:
if net_name2 not in vcg.keys():
vcg[net_name2] = []
# Skip yourself # Skip yourself
if net_name1 == net_name2: if net1.name == net2.name:
continue continue
if self.vcg_nets_overlap(nets[net_name1],
nets[net_name2]): if net1.pins_overlap(net2, pitch):
vcg[net_name2].append(net_name1) vcg[net2.name].add(net1.name)
current_offset = self.offset # Check if there are any cycles net1 <---> net2 in the VCG
# list of routes to do
while vcg: # Some of the pins may be to the left/below the channel offset,
# so adjust if this is the case
self.min_value = min([n.min_value for n in nets])
self.max_value = min([n.max_value for n in nets])
if self.vertical:
real_channel_offset = vector(self.offset.x, min(self.min_value, self.offset.y))
else:
real_channel_offset = vector(min(self.min_value, self.offset.x), self.offset.y)
current_offset = real_channel_offset
# Sort nets by left edge value
nets.sort()
while len(nets) > 0:
current_offset_value = current_offset.y if self.vertical else current_offset.x
# from pprint import pformat # from pprint import pformat
# print("VCG:\n", pformat(vcg)) # print("VCG:\n", pformat(vcg))
# for name,net in vcg.items():
# print(name, net.min_value, net.max_value, net.conflicts)
# print(current_offset)
# get a route from conflict graph with empty fanout set # get a route from conflict graph with empty fanout set
net_name = None for net in nets:
for net_name, conflicts in vcg.items(): # If it has no conflicts and the interval is to the right of the current offset in the track
if len(conflicts) == 0: if net.min_value >= current_offset_value and len(vcg[net.name]) == 0:
vcg = self.remove_net_from_graph(net_name, vcg) # print("Routing {}".format(net.name))
# Add the trunk routes from the bottom up for
# horizontal or the left to right for vertical
if self.vertical:
self.add_vertical_trunk_route(net.pins,
current_offset,
self.vertical_nonpref_pitch)
current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch)
else:
self.add_horizontal_trunk_route(net.pins,
current_offset,
self.horizontal_nonpref_pitch)
current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y)
# Remove the net from other constriants in the VCG
vcg = self.remove_net_from_graph(net.name, vcg)
nets.remove(net)
break break
else: else:
# FIXME: We don't support cyclic VCGs right now. # If we made a full pass and the offset didn't change...
debug.error("Cyclic VCG in channel router.", -1) current_offset_value = current_offset.y if self.vertical else current_offset.x
initial_offset_value = real_channel_offset.y if self.vertical else real_channel_offset.x
# These are the pins we'll have to connect if current_offset_value == initial_offset_value:
pin_list = nets[net_name] debug.info(0, "Channel offset: {}".format(real_channel_offset))
# print("Routing:", net_name, [x.name for x in pin_list]) debug.info(0, "Current offset: {}".format(current_offset))
debug.info(0, "VCG {}".format(str(vcg)))
# Remove the net from other constriants in the VCG debug.info(0, "HCG {}".format(str(hcg)))
vcg = self.remove_net_from_graph(net_name, vcg) for net in nets:
debug.info(0, "{0} pin: {1}".format(net.name, str(net.pins)))
# Add the trunk routes from the bottom up for if self.parent:
# horizontal or the left to right for vertical debug.info(0, "Saving vcg.gds")
if self.vertical: self.parent.gds_write("vcg.gds")
self.add_vertical_trunk_route(pin_list, debug.error("Cyclic VCG in channel router.", -1)
current_offset,
self.vertical_nonpref_pitch)
# This accounts for the via-to-via spacings
current_offset += vector(self.horizontal_nonpref_pitch, 0)
else:
self.add_horizontal_trunk_route(pin_list,
current_offset,
self.horizontal_nonpref_pitch)
# This accounts for the via-to-via spacings
current_offset += vector(0, self.vertical_nonpref_pitch)
# Increment the track and reset the offset to the start (like a typewriter)
if self.vertical:
current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, real_channel_offset.y)
else:
current_offset = vector(real_channel_offset.x, current_offset.y + self.vertical_nonpref_pitch)
# Return the size of the channel # Return the size of the channel
if self.vertical: if self.vertical:
self.width = 0 self.width = current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
self.height = current_offset.y self.height = self.max_value + self.vertical_nonpref_pitch - self.offset.y
return current_offset.y + self.vertical_nonpref_pitch - self.offset.y
else: else:
self.width = current_offset.x self.width = self.max_value + self.horizontal_nonpref_pitch - self.offset.x
self.height = 0 self.height = current_offset.y + self.vertical_nonpref_pitch - self.offset.y
return current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
def get_layer_pitch(self, layer): def get_layer_pitch(self, layer):
""" Return the track pitch on a given layer """ """ Return the track pitch on a given layer """
try: try:
@ -226,6 +317,17 @@ class channel_route(design.design):
self.add_path(self.vertical_layer, self.add_path(self.vertical_layer,
[vector(min_x - half_layer_width, trunk_offset.y), [vector(min_x - half_layer_width, trunk_offset.y),
vector(max_x + half_layer_width, trunk_offset.y)]) vector(max_x + half_layer_width, trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
if pin.cy() < trunk_offset.y:
pin_pos = pin.uc()
else:
pin_pos = pin.bc()
# No bend needed here
mid = vector(pin_pos.x, trunk_offset.y)
self.add_path(self.vertical_layer, [pin_pos, mid])
else: else:
# Add the horizontal trunk # Add the horizontal trunk
self.add_path(self.horizontal_layer, self.add_path(self.horizontal_layer,
@ -269,6 +371,17 @@ class channel_route(design.design):
self.add_path(self.horizontal_layer, self.add_path(self.horizontal_layer,
[vector(trunk_offset.x, min_y - half_layer_width), [vector(trunk_offset.x, min_y - half_layer_width),
vector(trunk_offset.x, max_y + half_layer_width)]) vector(trunk_offset.x, max_y + half_layer_width)])
# Route each pin to the trunk
for pin in pins:
# Find the correct side of the pin
if pin.cx() < trunk_offset.x:
pin_pos = pin.rc()
else:
pin_pos = pin.lc()
# No bend needed here
mid = vector(trunk_offset.x, pin_pos.y)
self.add_path(self.horizontal_layer, [pin_pos, mid])
else: else:
# Add the vertical trunk # Add the vertical trunk
self.add_path(self.vertical_layer, self.add_path(self.vertical_layer,
@ -292,18 +405,4 @@ class channel_route(design.design):
to_layer=self.horizontal_layer, to_layer=self.horizontal_layer,
offset=pin_pos) offset=pin_pos)
def vcg_pin_overlap(self, pin1, pin2, pitch):
""" Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin,
# for example, so the extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
# Pin 1 must be in the "LEFT" set
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap)
return overlaps

View File

@ -1018,7 +1018,7 @@ class layout():
Wrapper to create a vertical channel route Wrapper to create a vertical channel route
""" """
import channel_route import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True) cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self)
self.add_inst("vc", cr) self.add_inst("vc", cr)
self.connect_inst([]) self.connect_inst([])
@ -1027,7 +1027,7 @@ class layout():
Wrapper to create a horizontal channel route Wrapper to create a horizontal channel route
""" """
import channel_route import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False) cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
self.add_inst("hc", cr) self.add_inst("hc", cr)
self.connect_inst([]) self.connect_inst([])

View File

@ -185,9 +185,8 @@ class delay(simulation):
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9) self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9)
self.sen_meas.meta_str = sram_op.READ_ZERO self.sen_meas.meta_str = sram_op.READ_ZERO
self.sen_meas.meta_add_delay = True self.sen_meas.meta_add_delay = True
self.dout_volt_meas.append(self.sen_meas)
return self.dout_volt_meas + [self.sen_meas]
return self.dout_volt_meas
def create_read_bit_measures(self): def create_read_bit_measures(self):
""" Adds bit measurements for read0 and read1 cycles """ """ Adds bit measurements for read0 and read1 cycles """
@ -1351,7 +1350,7 @@ class delay(simulation):
Return the analytical model results for the SRAM. Return the analytical model results for the SRAM.
""" """
if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0:
debug.warning("Analytical characterization results are not supported for multiport.") debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay. # Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0'*self.addr_size, 0) self.set_probe('0'*self.addr_size, 0)

View File

@ -1,9 +1,9 @@
word_size = 2 word_size = 2
num_words = 16 num_words = 16
num_rw_ports = 1 num_rw_ports = 0
num_r_ports = 1 num_r_ports = 1
num_w_ports = 0 num_w_ports = 1
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corners_only = False

View File

@ -211,10 +211,11 @@ class bank(design.design):
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0) self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0)
# UPPER LEFT QUADRANT # UPPER LEFT QUADRANT
# To the left of the bitcell array # To the left of the bitcell array above the predecoders and control logic
x_offset = self.m2_gap + self.port_address.width x_offset = self.m2_gap + self.port_address.width
self.port_address_offsets[port] = vector(-x_offset, self.port_address_offsets[port] = vector(-x_offset,
self.main_bitcell_array_bottom) self.main_bitcell_array_bottom)
self.predecoder_height = self.port_address.predecoder_height + self.port_address_offsets[port].y
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver # Place the col decoder left aligned with wordline driver

View File

@ -314,24 +314,23 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8): for i in range(self.no_of_pre3x8):
self.place_pre3x8(i) self.place_pre3x8(i)
self.predecode_height = 0
if self.no_of_pre2x4 > 0:
self.predecode_height = self.pre2x4_inst[-1].uy()
if self.no_of_pre3x8 > 0:
self.predecode_height = self.pre3x8_inst[-1].uy()
def place_pre2x4(self, num): def place_pre2x4(self, num):
""" Place 2x4 predecoder to the left of the origin """ """ Place 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2): base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
base = vector(-self.pre2_4.width, 0)
else:
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
self.pre2x4_inst[num].place(base) self.pre2x4_inst[num].place(base)
def place_pre3x8(self, num): def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3): height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \
offset = vector(-self.pre_3_8.width, 0) + num * (self.pre3_8.height + self.predecoder_spacing)
else: offset = vector(-self.pre3_8.width, height)
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) + num * (self.pre3_8.height + self.predecoder_spacing)
offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset) self.pre3x8_inst[num].place(offset)
def create_row_decoder(self): def create_row_decoder(self):

View File

@ -153,6 +153,8 @@ class port_address(design.design):
wordline_driver_offset = vector(self.row_decoder.width, 0) wordline_driver_offset = vector(self.row_decoder.width, 0)
self.wordline_driver_inst.place(wordline_driver_offset) self.wordline_driver_inst.place(wordline_driver_offset)
self.row_decoder_inst.place(row_decoder_offset) self.row_decoder_inst.place(row_decoder_offset)
# Pass this up
self.predecoder_height = self.row_decoder.predecoder_height
self.height = self.row_decoder.height self.height = self.row_decoder.height
self.width = self.wordline_driver_inst.rx() self.width = self.wordline_driver_inst.rx()

View File

@ -372,66 +372,78 @@ class pgate(design.design):
self.width = width self.width = width
@staticmethod @staticmethod
def bin_width(tx_type, target_width): def best_bin(tx_type, target_width):
"""
Determine the width transistor that meets the accuracy requirement and is larger than target_width.
"""
# Find all of the relavent scaled bins and multiples
scaled_bins = pgate.scaled_bins(tx_type, target_width)
for (scaled_width, multiple) in scaled_bins:
if abs(target_width - scaled_width) / target_width <= 1 - OPTS.accuracy_requirement:
break
else:
debug.error("failed to bin tx size {}, try reducing accuracy requirement".format(target_width), 1)
debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type,
multiple,
scaled_width / multiple,
scaled_width,
target_width))
return(scaled_width / multiple, multiple)
@staticmethod
def scaled_bins(tx_type, target_width):
"""
Determine a set of widths and multiples that could be close to the right size
sorted by the fewest number of fingers.
"""
if tx_type == "nmos": if tx_type == "nmos":
bins = nmos_bins[drc("minwidth_poly")] bins = nmos_bins[drc("minwidth_poly")]
elif tx_type == "pmos": elif tx_type == "pmos":
bins = pmos_bins[drc("minwidth_poly")] bins = pmos_bins[drc("minwidth_poly")]
else: else:
debug.error("invalid tx type") debug.error("invalid tx type")
# Prune out bins that are too big, except for one bigger
bins = bins[0:bisect_left(bins, target_width) + 1] bins = bins[0:bisect_left(bins, target_width) + 1]
# Determine multiple of target width for each bin
if len(bins) == 1: if len(bins) == 1:
selected_bin = bins[0] scaled_bins = [(bins[0], math.ceil(target_width / bins[0]))]
scaling_factor = math.ceil(target_width / selected_bin)
scaled_bin = bins[0] * scaling_factor
else: else:
base_bins = []
scaled_bins = [] scaled_bins = []
scaling_factors = [] # Add the biggest size as 1x multiple
scaled_bins.append((bins[-1], 1))
# Compute discrete multiple of other sizes
for width in reversed(bins[:-1]):
multiple = math.ceil(target_width / width)
scaled_bins.append((multiple * width, multiple))
for width in bins: return(scaled_bins)
m = math.ceil(target_width / width)
base_bins.append(width)
scaling_factors.append(m)
scaled_bins.append(m * width)
select = -1
for i in reversed(range(0, len(scaled_bins))):
if abs(target_width - scaled_bins[i])/target_width <= 1-OPTS.accuracy_requirement:
select = i
break
if select == -1:
debug.error("failed to bin tx size {}, try reducing accuracy requirement".format(target_width), 1)
scaling_factor = scaling_factors[select]
scaled_bin = scaled_bins[select]
selected_bin = base_bins[select]
debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, selected_bin, scaling_factor, selected_bin * scaling_factor, target_width))
return(selected_bin, scaling_factor)
def permute_widths(self, tx_type, target_width):
@staticmethod
def nearest_bin(tx_type, target_width):
"""
Determine the nearest width to the given target_width
while assuming a single multiple.
"""
if tx_type == "nmos": if tx_type == "nmos":
bins = nmos_bins[drc("minwidth_poly")] bins = nmos_bins[drc("minwidth_poly")]
elif tx_type == "pmos": elif tx_type == "pmos":
bins = pmos_bins[drc("minwidth_poly")] bins = pmos_bins[drc("minwidth_poly")]
else: else:
debug.error("invalid tx type") debug.error("invalid tx type")
bins = bins[0:bisect_left(bins, target_width) + 1]
if len(bins) == 1:
scaled_bins = [(bins[0], math.ceil(target_width / bins[0]))]
else:
scaled_bins = []
scaled_bins.append((bins[-1], 1))
for width in bins[:-1]:
m = math.ceil(target_width / width)
scaled_bins.append((m * width, m))
return(scaled_bins) # Find the next larger bin
bin_loc = bisect_left(bins, target_width)
def bin_accuracy(self, ideal_width, width): if bin_loc < len(bins):
return 1-abs((ideal_width - width)/ideal_width) return bins[bin_loc]
else:
return bins[-1]
@staticmethod
def bin_accuracy(ideal_width, width):
return 1 - abs((ideal_width - width) / ideal_width)

View File

@ -14,15 +14,11 @@ from vector import vector
from math import ceil from math import ceil
from globals import OPTS from globals import OPTS
from utils import round_to_grid from utils import round_to_grid
from bisect import bisect_left
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
from errors import drc_error from errors import drc_error
if(OPTS.tech_name == "sky130"):
from tech import nmos_bins, pmos_bins
class pinv(pgate.pgate): class pinv(pgate.pgate):
""" """
Pinv generates gds of a parametrically sized inverter. The Pinv generates gds of a parametrically sized inverter. The
@ -164,8 +160,8 @@ class pinv(pgate.pgate):
else: else:
self.nmos_width = self.nmos_size * drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
nmos_bins = self.permute_widths("nmos", self.nmos_width) nmos_bins = self.scaled_bins("nmos", self.nmos_width)
pmos_bins = self.permute_widths("pmos", self.pmos_width) pmos_bins = self.scaled_bins("pmos", self.pmos_width)
valid_pmos = [] valid_pmos = []
for bin in pmos_bins: for bin in pmos_bins:

View File

@ -13,6 +13,7 @@ from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
class pinv_dec(pinv.pinv): class pinv_dec(pinv.pinv):
""" """
This is another version of pinv but with layout for the decoder. This is another version of pinv but with layout for the decoder.
@ -50,9 +51,8 @@ class pinv_dec(pinv.pinv):
self.nmos_width = self.nmos_size * drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
if OPTS.tech_name == "sky130": if OPTS.tech_name == "sky130":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) self.nmos_width = self.nearest_bin("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
return
# Over-ride the route input gate to call the horizontal version. # Over-ride the route input gate to call the horizontal version.
# Other top-level netlist and layout functions are not changed. # Other top-level netlist and layout functions are not changed.

View File

@ -39,8 +39,8 @@ class pnand2(pgate.pgate):
self.tx_mults = 1 self.tx_mults = 1
if OPTS.tech_name == "sky130": if OPTS.tech_name == "sky130":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) self.nmos_width = self.nearest_bin("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height, add_wells) pgate.pgate.__init__(self, name, height, add_wells)

View File

@ -42,8 +42,8 @@ class pnand3(pgate.pgate):
self.tx_mults = 1 self.tx_mults = 1
if OPTS.tech_name == "sky130": if OPTS.tech_name == "sky130":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) self.nmos_width = self.nearest_bin("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height, add_wells) pgate.pgate.__init__(self, name, height, add_wells)

View File

@ -38,8 +38,8 @@ class pnor2(pgate.pgate):
self.tx_mults = 1 self.tx_mults = 1
if OPTS.tech_name == "sky130": if OPTS.tech_name == "sky130":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) self.nmos_width = self.nearest_bin("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height, add_wells) pgate.pgate.__init__(self, name, height, add_wells)

View File

@ -9,11 +9,10 @@ import contact
import design import design
import debug import debug
from pgate import pgate from pgate import pgate
from tech import parameter from tech import parameter, drc
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
from tech import drc, layer
class precharge(design.design): class precharge(design.design):
@ -81,7 +80,7 @@ class precharge(design.design):
Initializes the upper and lower pmos Initializes the upper and lower pmos
""" """
if(OPTS.tech_name == "sky130"): if(OPTS.tech_name == "sky130"):
(self.ptx_width, self.ptx_mults) = pgate.bin_width("pmos", self.ptx_width) self.ptx_width = pgate.nearest_bin("pmos", self.ptx_width)
self.pmos = factory.create(module_type="ptx", self.pmos = factory.create(module_type="ptx",
width=self.ptx_width, width=self.ptx_width,
mults=self.ptx_mults, mults=self.ptx_mults,

View File

@ -131,7 +131,7 @@ class ptx(design.design):
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre":
# sky130 simulation cannot use the mult parameter in simulation # sky130 simulation cannot use the mult parameter in simulation
(self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width) (self.tx_width, self.mults) = pgate.best_bin(self.tx_type, self.tx_width)
main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type],
self.mults, self.mults,
self.tx_width, self.tx_width,

View File

@ -100,9 +100,9 @@ class sram():
import verify import verify
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
# Output the extracted design if requested # Output the extracted design if requested
sp_file = OPTS.output_path + "temp_pex.sp" pexname = OPTS.output_path + self.s.name + ".pex.sp"
spname = OPTS.output_path + self.s.name + ".sp" spname = OPTS.output_path + self.s.name + ".sp"
verify.run_pex(self.s.name, gdsname, spname, output=sp_file) verify.run_pex(self.s.name, gdsname, spname, output=pexname)
print_time("Extraction", datetime.datetime.now(), start_time) print_time("Extraction", datetime.datetime.now(), start_time)
else: else:
# Use generated spice file for characterization # Use generated spice file for characterization

View File

@ -10,6 +10,8 @@ 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 globals import OPTS from globals import OPTS
import channel_route
class sram_1bank(sram_base): class sram_1bank(sram_base):
""" """
@ -58,12 +60,14 @@ class sram_1bank(sram_base):
# the sense amps/column mux and cell array) # the sense amps/column mux and cell array)
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
# up to the row address DFFs. # up to the row address DFFs.
control_pos = [None] * len(self.all_ports) self.control_pos = [None] * len(self.all_ports)
row_addr_pos = [None] * len(self.all_ports) self.row_addr_pos = [None] * len(self.all_ports)
col_addr_pos = [None] * len(self.all_ports)
wmask_pos = [None] * len(self.all_ports) # DFFs are placd on their own
spare_wen_pos = [None] * len(self.all_ports) self.col_addr_pos = [None] * len(self.all_ports)
data_pos = [None] * len(self.all_ports) self.wmask_pos = [None] * len(self.all_ports)
self.spare_wen_pos = [None] * len(self.all_ports)
self.data_pos = [None] * len(self.all_ports)
# These positions utilize the channel route sizes. # These positions utilize the channel route sizes.
# FIXME: Auto-compute these rather than manual computation. # FIXME: Auto-compute these rather than manual computation.
@ -75,9 +79,11 @@ class sram_1bank(sram_base):
# Spare wen are on a separate layer so not included # Spare wen are on a separate layer so not included
# Start with 1 track minimum # Start with 1 track minimum
self.data_bus_size = [1] * len(self.all_ports) self.data_bus_size = [1] * len(self.all_ports)
self.col_addr_bus_size = [1] * len(self.all_ports)
for port in self.all_ports: for port in self.all_ports:
# The column address wires are routed separately from the data bus and will always be smaller.
# All ports need the col addr flops # All ports need the col addr flops
self.data_bus_size[port] += self.col_addr_size self.col_addr_bus_size[port] = self.col_addr_size * self.m4_nonpref_pitch
# Write ports need the data input flops and write mask flops # Write ports need the data input flops and write mask flops
if port in self.write_ports: if port in self.write_ports:
self.data_bus_size[port] += self.num_wmasks + self.word_size self.data_bus_size[port] += self.num_wmasks + self.word_size
@ -89,119 +95,152 @@ class sram_1bank(sram_base):
self.data_bus_size[port] *= self.m4_nonpref_pitch self.data_bus_size[port] *= self.m4_nonpref_pitch
# Add the gap in unit length # Add the gap in unit length
self.data_bus_size[port] += self.data_bus_gap self.data_bus_size[port] += self.data_bus_gap
# Port 0
port = 0
# This includes 2 M2 pitches for the row addr clock line. # The control and row addr flops are independent of any bus widths.
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data self.place_control()
# using the control_logic_center value. self.place_row_addr_dffs()
control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) # Place with an initial wide channel (from above)
self.control_logic_insts[port].place(control_pos[port]) self.place_dffs()
# Route the channel and set to the new data bus size
self.route_dffs(add_routes=False)
# Re-place with the new channel size
self.place_dffs()
# Now route the channel
self.route_dffs()
def place_row_addr_dffs(self):
"""
Must be run after place control logic.
"""
port = 0
# The row address bits are placed above the control logic aligned on the right. # The row address bits are placed above the control logic aligned on the right.
x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width
# It is above the control logic but below the top of the bitcell array # It is above the control logic but below the top of the bitcell array
y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_height)
row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port]) self.row_addr_dff_insts[port].place(self.row_addr_pos[port])
if len(self.all_ports)>1:
port = 1
# The row address bits are placed above the control logic aligned on the left.
x_offset = self.control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
# If it can be placed above the predecoder and below the control logic, do it
y_offset = self.bank.bank_array_ll.y
self.row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(self.row_addr_pos[port], mirror="XY")
def place_control(self):
port = 0
# This includes 2 M2 pitches for the row addr clock line.
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data
# using the control_logic_center value.
self.control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(self.control_pos[port])
if len(self.all_ports) > 1:
port = 1
# This includes 2 M2 pitches for the row addr clock line
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data
# using the control_logic_center value.
self.control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch,
self.bank.bank_array_ur.y
+ self.control_logic_insts[port].height
- self.control_logic_insts[port].height
+ self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(self.control_pos[port], mirror="XY")
def place_dffs(self):
"""
Place the col addr, data, wmask, and spare data DFFs.
This can be run more than once after we recompute the channel width.
"""
port = 0
# Add the col address flops below the bank to the right of the control logic # Add the col address flops below the bank to the right of the control logic
x_offset = self.control_logic_insts[port].rx() + self.dff.width x_offset = self.control_logic_insts[port].rx() + self.dff.width
y_offset = - self.data_bus_size[port] - self.dff.height # Place it a data bus below the x-axis, but at least as low as the control logic to not block
# the control logic signals
y_offset = min(-self.data_bus_size[port] - self.dff.height,
self.control_logic_insts[port].by())
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos[port] = vector(x_offset, self.col_addr_pos[port] = vector(x_offset,
y_offset) y_offset)
self.col_addr_dff_insts[port].place(col_addr_pos[port]) self.col_addr_dff_insts[port].place(self.col_addr_pos[port])
x_offset = self.col_addr_dff_insts[port].rx() x_offset = self.col_addr_dff_insts[port].rx()
else: else:
col_addr_pos[port] = vector(x_offset, 0) self.col_addr_pos[port] = vector(x_offset, 0)
if port in self.write_ports: if port in self.write_ports:
if self.write_size: if self.write_size:
# Add the write mask flops below the write mask AND array. # Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(x_offset, self.wmask_pos[port] = vector(x_offset,
y_offset) y_offset)
self.wmask_dff_insts[port].place(wmask_pos[port]) self.wmask_dff_insts[port].place(self.wmask_pos[port])
x_offset = self.wmask_dff_insts[port].rx() x_offset = self.wmask_dff_insts[port].rx()
# Add the data flops below the write mask flops. # Add the data flops below the write mask flops.
data_pos[port] = vector(x_offset, self.data_pos[port] = vector(x_offset,
y_offset) y_offset)
self.data_dff_insts[port].place(data_pos[port]) self.data_dff_insts[port].place(self.data_pos[port])
x_offset = self.data_dff_insts[port].rx() x_offset = self.data_dff_insts[port].rx()
# Add spare write enable flops to the right of data flops since the spare columns # Add spare write enable flops to the right of data flops since the spare columns
# will be on the right # will be on the right
if self.num_spare_cols: if self.num_spare_cols:
spare_wen_pos[port] = vector(x_offset, self.spare_wen_pos[port] = vector(x_offset,
y_offset) y_offset)
self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port])
x_offset = self.spare_wen_dff_insts[port].rx() x_offset = self.spare_wen_dff_insts[port].rx()
else: else:
wmask_pos[port] = vector(x_offset, y_offset) self.wmask_pos[port] = vector(x_offset, y_offset)
data_pos[port] = vector(x_offset, y_offset) self.data_pos[port] = vector(x_offset, y_offset)
spare_wen_pos[port] = vector(x_offset, y_offset) self.spare_wen_pos[port] = vector(x_offset, y_offset)
if len(self.all_ports)>1: if len(self.all_ports) > 1:
# Port 1
port = 1 port = 1
# This includes 2 M2 pitches for the row addr clock line
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data
# using the control_logic_center value.
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch,
self.bank.bank_array_ur.y
+ self.control_logic_insts[port].height
- self.control_logic_insts[port].height
+ self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(control_pos[port], mirror="XY")
# The row address bits are placed above the control logic aligned on the left.
x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
# It is below the control logic but below the bottom of the bitcell array
y_offset = min(self.control_logic_insts[port].by(), self.bank_inst.by() + self.row_addr_dff_insts[port].height)
row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY")
# Add the col address flops below the bank to the right of the control logic # Add the col address flops below the bank to the right of the control logic
x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width
y_offset = self.bank.height + self.data_bus_size[port] + self.dff.height # Place it a data bus below the x-axis, but at least as high as the control logic to not block
# the control logic signals
y_offset = max(self.bank.height + self.data_bus_size[port] + self.dff.height,
self.control_logic_insts[port].uy() - self.dff.height)
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos[port] = vector(x_offset - self.col_addr_dff_insts[port].width, self.col_addr_pos[port] = vector(x_offset,
y_offset) y_offset)
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") self.col_addr_dff_insts[port].place(self.col_addr_pos[port], mirror="XY")
x_offset = self.col_addr_dff_insts[port].lx() x_offset = self.col_addr_dff_insts[port].lx()
else: else:
col_addr_pos[port] = vector(x_offset, y_offset) self.col_addr_pos[port] = vector(x_offset, y_offset)
if port in self.write_ports: if port in self.write_ports:
# Add spare write enable flops to the right of the data flops since the spare # Add spare write enable flops to the right of the data flops since the spare
# columns will be on the left # columns will be on the left
if self.num_spare_cols: if self.num_spare_cols:
spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width, self.spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width,
y_offset) y_offset)
self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port], mirror="MX")
x_offset = self.spare_wen_dff_insts[port].lx() x_offset = self.spare_wen_dff_insts[port].lx()
if self.write_size: if self.write_size:
# Add the write mask flops below the write mask AND array. # Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width, self.wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width,
y_offset) y_offset)
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") self.wmask_dff_insts[port].place(self.wmask_pos[port], mirror="MX")
x_offset = self.wmask_dff_insts[port].lx() x_offset = self.wmask_dff_insts[port].lx()
# Add the data flops below the write mask flops. # Add the data flops below the write mask flops.
data_pos[port] = vector(x_offset - self.data_dff_insts[port].width, self.data_pos[port] = vector(x_offset - self.data_dff_insts[port].width,
y_offset) y_offset)
self.data_dff_insts[port].place(data_pos[port], mirror="MX") self.data_dff_insts[port].place(self.data_pos[port], mirror="MX")
else: else:
wmask_pos[port] = vector(x_offset, y_offset) self.wmask_pos[port] = vector(x_offset, y_offset)
data_pos[port] = vector(x_offset, y_offset) self.data_pos[port] = vector(x_offset, y_offset)
spare_wen_pos[port] = vector(x_offset, y_offset) self.spare_wen_pos[port] = vector(x_offset, y_offset)
def add_layout_pins(self): def add_layout_pins(self):
""" """
Add the top-level pins for a single bank SRAM with control. Add the top-level pins for a single bank SRAM with control.
@ -343,20 +382,42 @@ class sram_1bank(sram_base):
self.route_row_addr_dff() self.route_row_addr_dff()
def route_dffs(self, add_routes=True):
for port in self.all_ports: for port in self.all_ports:
self.route_dff(port) self.route_dff(port, add_routes)
def route_dff(self, port): def route_dff(self, port, add_routes):
route_map = [] route_map = []
# column mux dff # column mux dff is routed on it's own since it is to the far end
# decoder inputs are min pitch M2, so need to use lower layer stack
if self.col_addr_size > 0: if self.col_addr_size > 0:
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
dff_pins = [self.col_addr_dff_insts[port].get_pin(x) for x in dff_names] dff_pins = [self.col_addr_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)] bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, dff_pins))) route_map.extend(list(zip(bank_pins, dff_pins)))
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m1_pitch)
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
cr = channel_route.channel_route(netlist=route_map,
offset=offset,
layer_stack=self.m1_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.connect_inst([])
else:
self.col_addr_bus_size[port] = cr.height
route_map = []
# wmask dff # wmask dff
if self.num_wmasks > 0 and port in self.write_ports: if self.num_wmasks > 0 and port in self.write_ports:
@ -384,35 +445,46 @@ class sram_1bank(sram_base):
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, sram_pins))) route_map.extend(list(zip(bank_pins, sram_pins)))
if self.num_wmasks > 0 and port in self.write_ports:
layer_stack = self.m3_stack
else:
layer_stack = self.m1_stack
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m1_pitch)
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
if len(route_map) > 0:
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack)
# Route these separately because sometimes the pin pitch on the write driver is too narrow for M3 (FreePDK45)
# spare wen dff # spare wen dff
if self.num_spare_cols > 0 and port in self.write_ports: if self.num_spare_cols > 0 and port in self.write_ports:
dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)]
dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map = zip(bank_pins, dff_pins) route_map.extend(list(zip(bank_pins, dff_pins)))
self.create_horizontal_channel_route(netlist=route_map,
if len(route_map) > 0:
if self.num_wmasks > 0 and port in self.write_ports:
layer_stack = self.m3_stack
else:
layer_stack = self.m1_stack
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m1_pitch)
cr = channel_route.channel_route(netlist=route_map,
offset=offset, offset=offset,
layer_stack=self.m1_stack) layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.connect_inst([])
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
cr = channel_route.channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.connect_inst([])
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
def route_clk(self): def route_clk(self):
""" Route the clock network """ """ Route the clock network """

View File

@ -129,7 +129,7 @@ class sram_base(design, verilog, lef):
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
# We only enable final verification if we have routed the design # We only enable final verification if we have routed the design
self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=True) self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc)
if not OPTS.is_unit_test: if not OPTS.is_unit_test:
print_time("Verification", datetime.datetime.now(), start_time) print_time("Verification", datetime.datetime.now(), start_time)

View File

@ -135,7 +135,7 @@ def write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name):
def write_calibre_pex_script(cell_name, extract, output, final_verification): def write_calibre_pex_script(cell_name, extract, output, final_verification):
""" Write a pex script that can either just extract the netlist or the netlist+parasitics """ """ Write a pex script that can either just extract the netlist or the netlist+parasitics """
if output == None: if output == None:
output = cell_name + ".pex.netlist" output = cell_name + ".pex.sp"
# check if lvs report has been done # check if lvs report has been done
# if not run drc and lvs # if not run drc and lvs
@ -195,11 +195,14 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds") filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds")
else: else:
# Copy file to local dir if it isn't already # Copy file to local dir if it isn't already
if not os.path.isfile(gds_name): if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(OPTS.output_path+os.path.basename(gds_name),gds_name) shutil.copy(gds_name, OPTS.openram_temp)
drc_runset = write_calibre_drc_script(cell_name, extract, final_verification, gds_name) drc_runset = write_calibre_drc_script(cell_name, extract, final_verification, gds_name)
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp + os.path.basename(gds_name))
(outfile, errfile, resultsfile) = run_script(cell_name, "drc") (outfile, errfile, resultsfile) = run_script(cell_name, "drc")
# check the result for these lines in the summary: # check the result for these lines in the summary:
@ -241,14 +244,10 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
lvs_runset = write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name) lvs_runset = write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name)
# Copy file to local dir if it isn't already # Copy file to local dir if it isn't already
# if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
# shutil.copy(gds_name, OPTS.openram_temp) shutil.copy(gds_name, OPTS.openram_temp)
# if os.path.dirname(sp_name)!=OPTS.openram_temp.rstrip('/'): if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)):
# shutil.copy(sp_name, OPTS.openram_temp) shutil.copy(sp_name, OPTS.openram_temp)
if not os.path.isfile(gds_name):
shutil.copy(OPTS.output_path+os.path.basename(gds_name), gds_name)
if not os.path.isfile(sp_name):
shutil.copy(OPTS.output_path+os.path.basename(sp_name), sp_name)
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs") (outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
@ -329,13 +328,14 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False)
global num_pex_runs global num_pex_runs
num_pex_runs += 1 num_pex_runs += 1
write_calibre_pex_script(cell_name,True,output,final_verification) write_calibre_pex_script(cell_name, True, output, final_verification)
# Copy file to local dir if it isn't already
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp + os.path.basename(gds_name)) shutil.copy(gds_name, OPTS.openram_temp)
if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)): if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)):
shutil.copy(sp_name, OPTS.openram_temp + os.path.basename(sp_name)) shutil.copy(sp_name, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(cell_name, "pex") (outfile, errfile, resultsfile) = run_script(cell_name, "pex")

View File

@ -20,7 +20,7 @@ pex_warned = False
def run_drc(cell_name, gds_name, extract=False, final_verification=False): def run_drc(cell_name, gds_name, extract=False, final_verification=False):
global drc_warned global drc_warned
if not drc_warned: if not drc_warned:
debug.warning("DRC unable to run.") debug.error("DRC unable to run.", -1)
drc_warned=True drc_warned=True
# Since we warned, return a failing test. # Since we warned, return a failing test.
return 1 return 1
@ -29,7 +29,7 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
def run_lvs(cell_name, gds_name, sp_name, final_verification=False): def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
global lvs_warned global lvs_warned
if not lvs_warned: if not lvs_warned:
debug.warning("LVS unable to run.") debug.error("LVS unable to run.", -1)
lvs_warned=True lvs_warned=True
# Since we warned, return a failing test. # Since we warned, return a failing test.
return 1 return 1
@ -38,7 +38,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
def run_pex(name, gds_name, sp_name, output=None, final_verification=False): def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
global pex_warned global pex_warned
if not pex_warned: if not pex_warned:
debug.warning("PEX unable to run.") debug.error("PEX unable to run.", -1)
pex_warned=True pex_warned=True
# Since we warned, return a failing test. # Since we warned, return a failing test.
return 1 return 1