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,113 +152,138 @@ 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 vcg_nets_overlap(self, net1, net2):
"""
Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps.
"""
if self.vertical:
pitch = self.horizontal_nonpref_pitch
else:
pitch = self.vertical_nonpref_pitch
for pin1 in net1:
for pin2 in net2:
if self.vcg_pin_overlap(pin1, pin2, pitch):
return True
return False
def route(self): def route(self):
# FIXME: Must extend this to a horizontal conflict graph # Create names for the nets for the graphs
# too if we want to minimize the nets = []
# number of tracks! index = 0
# hcg = {} # print(self.netlist)
for pin_list in self.netlist:
nets.append(channel_net("n{}".format(index), pin_list, self.vertical))
index += 1
# Create the (undirected) horizontal constraint graph
hcg = collections.OrderedDict()
for net1 in nets:
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) # Initialize the vertical conflict graph (vcg)
# and make a list of all pins # and make a list of all pins
vcg = collections.OrderedDict() 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:") # print("Nets:")
# for net_name in nets: # for net_name in nets:
# print(net_name, [x.name for x in nets[net_name]]) # print(net_name, [x.name for x in nets[net_name]])
# Find the vertical pin conflicts # Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now # FIXME: O(n^2) but who cares for now
for net_name1 in nets: if self.vertical:
if net_name1 not in vcg.keys(): pitch = self.horizontal_nonpref_pitch
vcg[net_name1] = [] else:
for net_name2 in nets: pitch = self.vertical_nonpref_pitch
if net_name2 not in vcg.keys():
vcg[net_name2] = [] for net in nets:
vcg[net.name] = set()
for net1 in nets:
for net2 in nets:
# 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]):
vcg[net_name2].append(net_name1)
current_offset = self.offset if net1.pins_overlap(net2, pitch):
vcg[net2.name].add(net1.name)
# Check if there are any cycles net1 <---> net2 in the 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
# list of routes to do
while vcg:
# 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
if current_offset_value == initial_offset_value:
debug.info(0, "Channel offset: {}".format(real_channel_offset))
debug.info(0, "Current offset: {}".format(current_offset))
debug.info(0, "VCG {}".format(str(vcg)))
debug.info(0, "HCG {}".format(str(hcg)))
for net in nets:
debug.info(0, "{0} pin: {1}".format(net.name, str(net.pins)))
if self.parent:
debug.info(0, "Saving vcg.gds")
self.parent.gds_write("vcg.gds")
debug.error("Cyclic VCG in channel router.", -1)
# These are the pins we'll have to connect # Increment the track and reset the offset to the start (like a typewriter)
pin_list = nets[net_name] if self.vertical:
# print("Routing:", net_name, [x.name for x in pin_list]) current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, real_channel_offset.y)
else:
# Remove the net from other constriants in the VCG current_offset = vector(real_channel_offset.x, current_offset.y + self.vertical_nonpref_pitch)
vcg = self.remove_net_from_graph(net_name, vcg)
# 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(pin_list,
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)
# 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 """
@ -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 return self.dout_volt_meas + [self.sen_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,8 +372,34 @@ 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":
@ -381,57 +407,43 @@ class pgate(design.design):
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]
if len(bins) == 1:
selected_bin = bins[0]
scaling_factor = math.ceil(target_width / selected_bin)
scaled_bin = bins[0] * scaling_factor
else: # Determine multiple of target width for each bin
base_bins = []
scaled_bins = []
scaling_factors = []
for width in 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):
if tx_type == "nmos":
bins = nmos_bins[drc("minwidth_poly")]
elif tx_type == "pmos":
bins = pmos_bins[drc("minwidth_poly")]
else:
debug.error("invalid tx type")
bins = bins[0:bisect_left(bins, target_width) + 1]
if len(bins) == 1: if len(bins) == 1:
scaled_bins = [(bins[0], math.ceil(target_width / bins[0]))] scaled_bins = [(bins[0], math.ceil(target_width / bins[0]))]
else: else:
scaled_bins = [] scaled_bins = []
# Add the biggest size as 1x multiple
scaled_bins.append((bins[-1], 1)) scaled_bins.append((bins[-1], 1))
for width in bins[:-1]: # Compute discrete multiple of other sizes
m = math.ceil(target_width / width) for width in reversed(bins[:-1]):
scaled_bins.append((m * width, m)) multiple = math.ceil(target_width / width)
scaled_bins.append((multiple * width, multiple))
return(scaled_bins) return(scaled_bins)
def bin_accuracy(self, ideal_width, width): @staticmethod
return 1-abs((ideal_width - width)/ideal_width) 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":
bins = nmos_bins[drc("minwidth_poly")]
elif tx_type == "pmos":
bins = pmos_bins[drc("minwidth_poly")]
else:
debug.error("invalid tx type")
# Find the next larger bin
bin_loc = bisect_left(bins, target_width)
if bin_loc < len(bins):
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,14 +14,10 @@ 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):
""" """
@ -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
@ -90,117 +96,150 @@ class sram_1bank(sram_base):
# 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 # The control and row addr flops are independent of any bus widths.
self.place_control()
self.place_row_addr_dffs()
# Place with an initial wide channel (from above)
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.
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
y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_height)
self.row_addr_pos[port] = vector(x_offset, y_offset)
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 port = 0
# This includes 2 M2 pitches for the row addr clock line. # 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 # 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. # using the control_logic_center value.
control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch, 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.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(control_pos[port]) 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")
# The row address bits are placed above the control logic aligned on the right. def place_dffs(self):
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 Place the col addr, data, wmask, and spare data DFFs.
y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) This can be run more than once after we recompute the channel width.
row_addr_pos[port] = vector(x_offset, y_offset) """
self.row_addr_dff_insts[port].place(row_addr_pos[port])
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):
""" """
@ -343,14 +382,17 @@ class sram_1bank(sram_base):
self.route_row_addr_dff() self.route_row_addr_dff()
for port in self.all_ports: def route_dffs(self, add_routes=True):
self.route_dff(port)
def route_dff(self, port): for port in self.all_ports:
self.route_dff(port, add_routes)
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]
@ -358,6 +400,25 @@ 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, 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:
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
@ -384,34 +445,45 @@ 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,12 +328,13 @@ 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