mirror of https://github.com/VLSIDA/OpenRAM.git
clean version
This commit is contained in:
parent
0ae194f17c
commit
6743049d44
|
|
@ -5,6 +5,8 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
# This version aims to keep the track out/vertical to the dff pins
|
||||||
|
# Now only consider the channel at the south, but works fine with channel at the north
|
||||||
import collections
|
import collections
|
||||||
from openram import debug
|
from openram import debug
|
||||||
from openram.tech import drc
|
from openram.tech import drc
|
||||||
|
|
@ -85,7 +87,8 @@ class channel_route(design):
|
||||||
layer_stack,
|
layer_stack,
|
||||||
directions=None,
|
directions=None,
|
||||||
vertical=False,
|
vertical=False,
|
||||||
parent=None):
|
parent=None,
|
||||||
|
dff_area=False):
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
|
@ -106,6 +109,7 @@ class channel_route(design):
|
||||||
self.vertical = vertical
|
self.vertical = vertical
|
||||||
# For debugging...
|
# For debugging...
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
self.dff_area = dff_area # this is a special value to handle dff areas, should be true when routing col_dff/dffs
|
||||||
|
|
||||||
if not directions or directions == "pref":
|
if not directions or directions == "pref":
|
||||||
# Use the preferred layer directions
|
# Use the preferred layer directions
|
||||||
|
|
@ -139,6 +143,16 @@ class channel_route(design):
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
||||||
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
|
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
|
||||||
|
# For debug
|
||||||
|
|
||||||
|
debug.warning("layer horizontal: {0}".format(self.horizontal_layer))
|
||||||
|
debug.warning("horizontal_nonpref_pitch: {0}".format(self.horizontal_nonpref_pitch))
|
||||||
|
debug.warning("horizontal_pitch: {0}".format(self.horizontal_pitch))
|
||||||
|
debug.warning("horizontal_space: {0}".format(self.horizontal_space))
|
||||||
|
debug.warning("layer vertical: {0}".format(self.vertical_layer))
|
||||||
|
debug.warning("vertiacl_nonpref_pitch: {0}".format(self.vertical_pitch))
|
||||||
|
debug.warning("vertical_pitch: {0}".format(self.vertical_pitch))
|
||||||
|
debug.warning("vertical_space: {0}".format(self.vertical_space))
|
||||||
|
|
||||||
self.route()
|
self.route()
|
||||||
|
|
||||||
|
|
@ -219,8 +233,19 @@ class channel_route(design):
|
||||||
real_channel_offset = vector(self.offset.x, min(self.min_value, self.offset.y))
|
real_channel_offset = vector(self.offset.x, min(self.min_value, self.offset.y))
|
||||||
else:
|
else:
|
||||||
real_channel_offset = vector(min(self.min_value, self.offset.x), self.offset.y)
|
real_channel_offset = vector(min(self.min_value, self.offset.x), self.offset.y)
|
||||||
current_offset = real_channel_offset
|
|
||||||
|
|
||||||
|
if self.dff_area == False:
|
||||||
|
current_offset = real_channel_offset
|
||||||
|
else: # special handle for dff area
|
||||||
|
current_offset = vector(real_channel_offset.x, real_channel_offset.y + 5) # make route out of dffs area
|
||||||
|
|
||||||
|
if self.layer_stack == self.m2_stack:
|
||||||
|
self.vertical_nonpref_pitch = self.horizontal_pitch
|
||||||
|
|
||||||
|
if self.layer_stack == self.m1_stack:
|
||||||
|
current_offset = vector(real_channel_offset.x, current_offset.y + 14) # make sure no overlap between col_dffs & data_dffs
|
||||||
|
if real_channel_offset.y > 0: # which means this is channnel router for coldff at the top
|
||||||
|
current_offset = real_channel_offset # no offset to avoid overlap problem at the top
|
||||||
# Sort nets by left edge value
|
# Sort nets by left edge value
|
||||||
nets.sort()
|
nets.sort()
|
||||||
while len(nets) > 0:
|
while len(nets) > 0:
|
||||||
|
|
|
||||||
|
|
@ -1,427 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
# This version aims to keep the track out/vertical to the dff pins
|
|
||||||
# Now only consider the channel at the south
|
|
||||||
import collections
|
|
||||||
from openram import debug
|
|
||||||
from openram.tech import drc
|
|
||||||
from .vector import vector
|
|
||||||
from .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):
|
|
||||||
|
|
||||||
unique_id = 0
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
netlist,
|
|
||||||
offset,
|
|
||||||
layer_stack,
|
|
||||||
directions=None,
|
|
||||||
vertical=False,
|
|
||||||
parent=None):
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
routing channel will start. This does NOT try to minimize the
|
|
||||||
number of tracks -- instead, it picks an order to avoid the
|
|
||||||
vertical conflicts between pins. The track size must be the number of
|
|
||||||
nets times the *nonpreferred* routing of the non-track layer pitch.
|
|
||||||
|
|
||||||
"""
|
|
||||||
name = "cr_{0}".format(channel_route.unique_id)
|
|
||||||
channel_route.unique_id += 1
|
|
||||||
super().__init__(name)
|
|
||||||
|
|
||||||
self.netlist = netlist
|
|
||||||
self.offset = offset
|
|
||||||
self.layer_stack = layer_stack
|
|
||||||
self.directions = directions
|
|
||||||
self.vertical = vertical
|
|
||||||
# For debugging...
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
if not directions or directions == "pref":
|
|
||||||
# Use the preferred layer directions
|
|
||||||
if self.get_preferred_direction(layer_stack[0]) == "V":
|
|
||||||
self.vertical_layer = layer_stack[0]
|
|
||||||
self.horizontal_layer = layer_stack[2]
|
|
||||||
else:
|
|
||||||
self.vertical_layer = layer_stack[2]
|
|
||||||
self.horizontal_layer = layer_stack[0]
|
|
||||||
elif directions == "nonpref":
|
|
||||||
# Use the preferred layer directions
|
|
||||||
if self.get_preferred_direction(layer_stack[0]) == "V":
|
|
||||||
self.vertical_layer = layer_stack[2]
|
|
||||||
self.horizontal_layer = layer_stack[0]
|
|
||||||
else:
|
|
||||||
self.vertical_layer = layer_stack[0]
|
|
||||||
self.horizontal_layer = layer_stack[2]
|
|
||||||
else:
|
|
||||||
# Use the layer directions specified to the router rather than
|
|
||||||
# the preferred directions
|
|
||||||
debug.check(directions[0] != directions[1], "Must have unique layer directions.")
|
|
||||||
if directions[0] == "V":
|
|
||||||
self.vertical_layer = layer_stack[0]
|
|
||||||
self.horizontal_layer = layer_stack[2]
|
|
||||||
else:
|
|
||||||
self.horizontal_layer = layer_stack[0]
|
|
||||||
self.vertical_layer = layer_stack[2]
|
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.vertical_layer)
|
|
||||||
(self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
|
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
|
||||||
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
|
|
||||||
# For debug
|
|
||||||
|
|
||||||
debug.warning("layer horizontal: {0}".format(self.horizontal_layer))
|
|
||||||
debug.warning("horizontal_nonpref_pitch: {0}".format(self.horizontal_nonpref_pitch))
|
|
||||||
debug.warning("horizontal_pitch: {0}".format(self.horizontal_pitch))
|
|
||||||
debug.warning("horizontal_space: {0}".format(self.horizontal_space))
|
|
||||||
debug.warning("layer vertical: {0}".format(self.vertical_layer))
|
|
||||||
debug.warning("vertiacl_nonpref_pitch: {0}".format(self.vertical_pitch))
|
|
||||||
debug.warning("vertical_pitch: {0}".format(self.vertical_pitch))
|
|
||||||
debug.warning("vertical_space: {0}".format(self.vertical_space))
|
|
||||||
|
|
||||||
self.route()
|
|
||||||
|
|
||||||
def remove_net_from_graph(self, pin, g):
|
|
||||||
"""
|
|
||||||
Remove the pin from the graph and all conflicts
|
|
||||||
"""
|
|
||||||
g.pop(pin, None)
|
|
||||||
|
|
||||||
# Remove the pin from all conflicts
|
|
||||||
# FIXME: This is O(n^2), so maybe optimize it.
|
|
||||||
for other_pin, conflicts in g.items():
|
|
||||||
if pin in conflicts:
|
|
||||||
g[other_pin].remove(pin)
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
# 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:
|
|
||||||
pitch = self.horizontal_nonpref_pitch
|
|
||||||
else:
|
|
||||||
pitch = self.vertical_nonpref_pitch
|
|
||||||
|
|
||||||
for net in nets:
|
|
||||||
vcg[net.name] = set()
|
|
||||||
|
|
||||||
for net1 in nets:
|
|
||||||
for net2 in nets:
|
|
||||||
# Skip yourself
|
|
||||||
if net1.name == net2.name:
|
|
||||||
continue
|
|
||||||
|
|
||||||
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
|
|
||||||
current_offset = vector(real_channel_offset.x, current_offset.y + 5) # make route out of dffs area
|
|
||||||
|
|
||||||
if self.layer_stack == self.m2_stack:
|
|
||||||
self.vertical_nonpref_pitch = self.horizontal_pitch
|
|
||||||
|
|
||||||
if self.layer_stack == self.m1_stack:
|
|
||||||
current_offset = vector(real_channel_offset.x, current_offset.y + 14) # make sure no overlap between col_dffs & data_dffs
|
|
||||||
if real_channel_offset.y > 0: # which means this is channnel router for coldff at the top
|
|
||||||
current_offset = real_channel_offset # no offset to avoid overlap problem at the top
|
|
||||||
# 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
|
|
||||||
# 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
|
|
||||||
for net in nets:
|
|
||||||
# If it has no conflicts and the interval is to the right of the current offset in the track
|
|
||||||
if net.min_value >= current_offset_value and len(vcg[net.name]) == 0:
|
|
||||||
# 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.horizontal_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.vertical_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
|
|
||||||
else:
|
|
||||||
# If we made a full pass and the offset didn't change...
|
|
||||||
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)
|
|
||||||
|
|
||||||
# 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
|
|
||||||
if self.vertical:
|
|
||||||
self.width = current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
|
|
||||||
self.height = self.max_value + self.vertical_nonpref_pitch - self.offset.y
|
|
||||||
else:
|
|
||||||
self.width = self.max_value + self.horizontal_nonpref_pitch - self.offset.x
|
|
||||||
self.height = current_offset.y + self.vertical_nonpref_pitch - self.offset.y
|
|
||||||
|
|
||||||
def get_layer_pitch(self, layer):
|
|
||||||
""" Return the track pitch on a given layer """
|
|
||||||
try:
|
|
||||||
# FIXME: Using non-pref pitch here due to overlap bug in VCG constraints.
|
|
||||||
# It should just result in inefficient channel width but will work.
|
|
||||||
pitch = getattr(self, "{}_pitch".format(layer))
|
|
||||||
nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer))
|
|
||||||
space = getattr(self, "{}_space".format(layer))
|
|
||||||
except AttributeError:
|
|
||||||
debug.error("Cannot find layer pitch.", -1)
|
|
||||||
return (nonpref_pitch, pitch, pitch - space, space)
|
|
||||||
|
|
||||||
def add_horizontal_trunk_route(self,
|
|
||||||
pins,
|
|
||||||
trunk_offset,
|
|
||||||
pitch):
|
|
||||||
"""
|
|
||||||
Create a trunk route for all pins with
|
|
||||||
the trunk located at the given y offset.
|
|
||||||
"""
|
|
||||||
max_x = max([pin.center().x for pin in pins])
|
|
||||||
min_x = min([pin.center().x for pin in pins])
|
|
||||||
|
|
||||||
# if we are less than a pitch, just create a non-preferred layer jog
|
|
||||||
non_preferred_route = max_x - min_x <= pitch
|
|
||||||
|
|
||||||
if non_preferred_route:
|
|
||||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
|
|
||||||
# Add the horizontal trunk on the vertical layer!
|
|
||||||
self.add_path(self.vertical_layer,
|
|
||||||
[vector(min_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:
|
|
||||||
# Add the horizontal trunk
|
|
||||||
self.add_path(self.horizontal_layer,
|
|
||||||
[vector(min_x, trunk_offset.y),
|
|
||||||
vector(max_x, trunk_offset.y)])
|
|
||||||
|
|
||||||
# Route each pin to the trunk
|
|
||||||
for pin in pins:
|
|
||||||
# Find the correct side of the pin
|
|
||||||
if pin.cy() < trunk_offset.y:
|
|
||||||
pin_pos = pin.uc()
|
|
||||||
else:
|
|
||||||
pin_pos = pin.bc()
|
|
||||||
mid = vector(pin_pos.x, trunk_offset.y)
|
|
||||||
self.add_path(self.vertical_layer, [pin_pos, mid])
|
|
||||||
if not non_preferred_route:
|
|
||||||
self.add_via_center(layers=self.layer_stack,
|
|
||||||
offset=mid,
|
|
||||||
directions=self.directions)
|
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
|
||||||
to_layer=self.vertical_layer,
|
|
||||||
offset=pin_pos)
|
|
||||||
|
|
||||||
def add_vertical_trunk_route(self,
|
|
||||||
pins,
|
|
||||||
trunk_offset,
|
|
||||||
pitch):
|
|
||||||
"""
|
|
||||||
Create a trunk route for all pins with the
|
|
||||||
trunk located at the given x offset.
|
|
||||||
"""
|
|
||||||
max_y = max([pin.center().y for pin in pins])
|
|
||||||
min_y = min([pin.center().y for pin in pins])
|
|
||||||
|
|
||||||
# if we are less than a pitch, just create a non-preferred layer jog
|
|
||||||
non_preferred_route = max_y - min_y <= pitch
|
|
||||||
|
|
||||||
if non_preferred_route:
|
|
||||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
|
|
||||||
# Add the vertical trunk on the horizontal layer!
|
|
||||||
self.add_path(self.horizontal_layer,
|
|
||||||
[vector(trunk_offset.x, min_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:
|
|
||||||
# Add the vertical trunk
|
|
||||||
self.add_path(self.vertical_layer,
|
|
||||||
[vector(trunk_offset.x, min_y),
|
|
||||||
vector(trunk_offset.x, max_y)])
|
|
||||||
|
|
||||||
# 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()
|
|
||||||
mid = vector(trunk_offset.x, pin_pos.y)
|
|
||||||
self.add_path(self.horizontal_layer, [pin_pos, mid])
|
|
||||||
if not non_preferred_route:
|
|
||||||
self.add_via_center(layers=self.layer_stack,
|
|
||||||
offset=mid,
|
|
||||||
directions=self.directions)
|
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
|
||||||
to_layer=self.horizontal_layer,
|
|
||||||
offset=pin_pos)
|
|
||||||
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
# This aim to change the position of the submodule
|
||||||
import datetime
|
import datetime
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from importlib import import_module, reload
|
from importlib import import_module, reload
|
||||||
|
|
@ -206,15 +207,15 @@ class sram_1bank(design, verilog, lef):
|
||||||
if not OPTS.is_unit_test:
|
if not OPTS.is_unit_test:
|
||||||
print_time("Submodules", datetime.datetime.now(), start_time)
|
print_time("Submodules", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self, position_add=0, mod=0):
|
||||||
""" Layout creation """
|
""" Layout creation """
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
self.place_instances()
|
self.place_instances_changeable(position_add=position_add)
|
||||||
if not OPTS.is_unit_test:
|
if not OPTS.is_unit_test:
|
||||||
print_time("Placement", datetime.datetime.now(), start_time)
|
print_time("Placement", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
self.route_layout()
|
self.route_layout(mod=mod)
|
||||||
|
|
||||||
if not OPTS.is_unit_test:
|
if not OPTS.is_unit_test:
|
||||||
print_time("Routing", datetime.datetime.now(), start_time)
|
print_time("Routing", datetime.datetime.now(), start_time)
|
||||||
|
|
@ -318,7 +319,7 @@ class sram_1bank(design, verilog, lef):
|
||||||
# Grid is left with many top level pins
|
# Grid is left with many top level pins
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def route_escape_pins(self, bbox=None):
|
def route_escape_pins(self, bbox=None, mod=0):
|
||||||
"""
|
"""
|
||||||
Add the top-level pins for a single bank SRAM with control.
|
Add the top-level pins for a single bank SRAM with control.
|
||||||
"""
|
"""
|
||||||
|
|
@ -362,9 +363,14 @@ class sram_1bank(design, verilog, lef):
|
||||||
pins_to_route.append("spare_wen{0}[{1}]".format(port, bit))
|
pins_to_route.append("spare_wen{0}[{1}]".format(port, bit))
|
||||||
|
|
||||||
from openram.router import signal_escape_router as router
|
from openram.router import signal_escape_router as router
|
||||||
|
# mod Use for control which edge/position the pins(dout) will be placed
|
||||||
|
# 0 -> default
|
||||||
|
# 1 -> all top/bottom
|
||||||
|
# 2 -> all left/right
|
||||||
rtr = router(layers=self.m3_stack,
|
rtr = router(layers=self.m3_stack,
|
||||||
bbox=bbox,
|
bbox=bbox,
|
||||||
design=self)
|
design=self,
|
||||||
|
mod=mod)
|
||||||
rtr.route(pins_to_route)
|
rtr.route(pins_to_route)
|
||||||
|
|
||||||
def compute_bus_sizes(self):
|
def compute_bus_sizes(self):
|
||||||
|
|
@ -779,10 +785,10 @@ class sram_1bank(design, verilog, lef):
|
||||||
else:
|
else:
|
||||||
self.num_spare_cols = 0
|
self.num_spare_cols = 0
|
||||||
|
|
||||||
def place_instances(self):
|
def place_instances_changeable(self, position_add=0):
|
||||||
"""
|
"""
|
||||||
This places the instances for a single bank SRAM with control
|
This places the instances for a single bank SRAM with control
|
||||||
logic and up to 2 ports.
|
logic and up to 2 ports, but be able to change the io the dff position
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# No orientation or offset
|
# No orientation or offset
|
||||||
|
|
@ -844,6 +850,10 @@ class sram_1bank(design, verilog, lef):
|
||||||
self.route_dffs(add_routes=False)
|
self.route_dffs(add_routes=False)
|
||||||
self.remove_layout_pins()
|
self.remove_layout_pins()
|
||||||
|
|
||||||
|
for port in self.all_ports:
|
||||||
|
# Add the extra position
|
||||||
|
self.data_bus_size[port] += position_add
|
||||||
|
|
||||||
# Re-place with the new channel size
|
# Re-place with the new channel size
|
||||||
self.place_dffs()
|
self.place_dffs()
|
||||||
|
|
||||||
|
|
@ -1051,7 +1061,7 @@ class sram_1bank(design, verilog, lef):
|
||||||
"spare_wen{0}[{1}]".format(port, bit),
|
"spare_wen{0}[{1}]".format(port, bit),
|
||||||
start_layer=pin_layer)
|
start_layer=pin_layer)
|
||||||
|
|
||||||
def route_layout(self):
|
def route_layout(self, mod=0):
|
||||||
""" Route a single bank SRAM """
|
""" Route a single bank SRAM """
|
||||||
|
|
||||||
self.route_clk()
|
self.route_clk()
|
||||||
|
|
@ -1075,7 +1085,8 @@ class sram_1bank(design, verilog, lef):
|
||||||
if OPTS.perimeter_pins:
|
if OPTS.perimeter_pins:
|
||||||
# We now route the escape routes far enough out so that they will
|
# We now route the escape routes far enough out so that they will
|
||||||
# reach past the power ring or stripes on the sides
|
# reach past the power ring or stripes on the sides
|
||||||
self.route_escape_pins(init_bbox)
|
self.route_escape_pins(bbox=init_bbox, mod=mod)
|
||||||
|
|
||||||
if OPTS.route_supplies:
|
if OPTS.route_supplies:
|
||||||
self.route_supplies(init_bbox)
|
self.route_supplies(init_bbox)
|
||||||
|
|
||||||
|
|
@ -1118,7 +1129,8 @@ class sram_1bank(design, verilog, lef):
|
||||||
cr = channel_route(netlist=route_map,
|
cr = channel_route(netlist=route_map,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layer_stack=layer_stack,
|
layer_stack=layer_stack,
|
||||||
parent=self)
|
parent=self,
|
||||||
|
dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs
|
||||||
# This causes problem in magic since it sometimes cannot extract connectivity of instances
|
# This causes problem in magic since it sometimes cannot extract connectivity of instances
|
||||||
# with no active devices.
|
# with no active devices.
|
||||||
self.add_inst(cr.name, cr)
|
self.add_inst(cr.name, cr)
|
||||||
|
|
@ -1130,7 +1142,8 @@ class sram_1bank(design, verilog, lef):
|
||||||
cr = channel_route(netlist=route_map,
|
cr = channel_route(netlist=route_map,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layer_stack=layer_stack,
|
layer_stack=layer_stack,
|
||||||
parent=self)
|
parent=self,
|
||||||
|
dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs
|
||||||
# This causes problem in magic since it sometimes cannot extract connectivity of instances
|
# This causes problem in magic since it sometimes cannot extract connectivity of instances
|
||||||
# with no active devices.
|
# with no active devices.
|
||||||
self.add_inst(cr.name, cr)
|
self.add_inst(cr.name, cr)
|
||||||
|
|
@ -1167,7 +1180,7 @@ class sram_1bank(design, verilog, lef):
|
||||||
if len(route_map) > 0:
|
if len(route_map) > 0:
|
||||||
|
|
||||||
# This layer stack must be different than the column addr dff layer stack
|
# This layer stack must be different than the column addr dff layer stack
|
||||||
layer_stack = self.m3_stack
|
layer_stack = self.m2_stack
|
||||||
if port == 0:
|
if port == 0:
|
||||||
# This is relative to the bank at 0,0 or the s_en which is routed on M3 also
|
# This is relative to the bank at 0,0 or the s_en which is routed on M3 also
|
||||||
if "s_en" in self.control_logic_insts[port].mod.pin_map:
|
if "s_en" in self.control_logic_insts[port].mod.pin_map:
|
||||||
|
|
@ -1181,7 +1194,8 @@ class sram_1bank(design, verilog, lef):
|
||||||
cr = channel_route(netlist=route_map,
|
cr = channel_route(netlist=route_map,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layer_stack=layer_stack,
|
layer_stack=layer_stack,
|
||||||
parent=self)
|
parent=self,
|
||||||
|
dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs
|
||||||
if add_routes:
|
if add_routes:
|
||||||
# This causes problem in magic since it sometimes cannot extract connectivity of instances
|
# This causes problem in magic since it sometimes cannot extract connectivity of instances
|
||||||
# with no active devices.
|
# with no active devices.
|
||||||
|
|
@ -1201,7 +1215,8 @@ class sram_1bank(design, verilog, lef):
|
||||||
cr = channel_route(netlist=route_map,
|
cr = channel_route(netlist=route_map,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layer_stack=layer_stack,
|
layer_stack=layer_stack,
|
||||||
parent=self)
|
parent=self,
|
||||||
|
dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs
|
||||||
if add_routes:
|
if add_routes:
|
||||||
# This causes problem in magic since it sometimes cannot extract connectivity of instances
|
# This causes problem in magic since it sometimes cannot extract connectivity of instances
|
||||||
# with no active devices.
|
# with no active devices.
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -17,7 +17,7 @@ class signal_escape_router(router):
|
||||||
This is the signal escape router that uses the Hanan grid graph method.
|
This is the signal escape router that uses the Hanan grid graph method.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, layers, design, bbox=None):
|
def __init__(self, layers, design, bbox=None, mod=0):
|
||||||
|
|
||||||
# `router` is the base router class
|
# `router` is the base router class
|
||||||
router.__init__(self, layers, design, bbox)
|
router.__init__(self, layers, design, bbox)
|
||||||
|
|
@ -25,6 +25,17 @@ class signal_escape_router(router):
|
||||||
# New pins are the side supply pins
|
# New pins are the side supply pins
|
||||||
self.new_pins = {}
|
self.new_pins = {}
|
||||||
|
|
||||||
|
# Use for add distance of dout pins at the perimeter
|
||||||
|
self.distance_right = 0
|
||||||
|
|
||||||
|
self.distance_left = 0
|
||||||
|
|
||||||
|
# Use for control which edge/position the pins(dout) will be placed
|
||||||
|
# 0 -> default
|
||||||
|
# 1 -> all top/bottom
|
||||||
|
# 2 -> all left/right
|
||||||
|
self.state_mod = mod
|
||||||
|
|
||||||
|
|
||||||
def route(self, pin_names):
|
def route(self, pin_names):
|
||||||
""" Route the given pins to the perimeter. """
|
""" Route the given pins to the perimeter. """
|
||||||
|
|
@ -76,8 +87,6 @@ class signal_escape_router(router):
|
||||||
self.find_vias(new_vias)
|
self.find_vias(new_vias)
|
||||||
routed_count += 1
|
routed_count += 1
|
||||||
debug.info(2, "Routed {} of {} signal pins".format(routed_count, routed_max))
|
debug.info(2, "Routed {} of {} signal pins".format(routed_count, routed_max))
|
||||||
print("route pins:")
|
|
||||||
print(source)
|
|
||||||
self.replace_layout_pins()
|
self.replace_layout_pins()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -177,18 +186,13 @@ class signal_escape_router(router):
|
||||||
rect=rect,
|
rect=rect,
|
||||||
layer_name_pp=layer)
|
layer_name_pp=layer)
|
||||||
self.fake_pins.append(pin)
|
self.fake_pins.append(pin)
|
||||||
print("this is add_per")
|
|
||||||
print(pin.name)
|
|
||||||
print(pin.center)
|
|
||||||
|
|
||||||
def create_fake_pin(self, pin):
|
def create_fake_pin(self, pin):
|
||||||
""" Create a fake pin on the perimeter orthogonal to the given pin. """
|
""" Create a fake pin on the perimeter orthogonal to the given pin. """
|
||||||
|
|
||||||
ll, ur = self.bbox
|
ll, ur = self.bbox
|
||||||
c = pin.center()
|
c = pin.center()
|
||||||
print("inside pin name")
|
|
||||||
print("----------------------------------------------------------")
|
|
||||||
print(pin.name)
|
|
||||||
# Find the closest edge
|
# Find the closest edge
|
||||||
edge, vertical = self.get_closest_edge(c)
|
edge, vertical = self.get_closest_edge(c)
|
||||||
|
|
||||||
|
|
@ -202,30 +206,49 @@ class signal_escape_router(router):
|
||||||
fake_center = vector(ur.x + self.track_wire * 2, c.y)
|
fake_center = vector(ur.x + self.track_wire * 2, c.y)
|
||||||
if edge == "top":
|
if edge == "top":
|
||||||
fake_center = vector(c.x, ur.y + self.track_wire * 2)
|
fake_center = vector(c.x, ur.y + self.track_wire * 2)
|
||||||
#fake_center = vector(ll.x - self.track_wire * 2, c.y) # test if here we could change the pin position at the layout
|
|
||||||
"""
|
# relocate the pin position
|
||||||
pattern = r'^addr0_1'
|
pattern = r'^dout'
|
||||||
if re.match(pattern, pin.name):
|
if re.match(pattern, pin.name):
|
||||||
vertical = True
|
|
||||||
fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_wire * 4)# fix still do not know how to control the distance between every fake pin
|
if self.state_mod == 0:
|
||||||
#do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y?
|
pass# do not change, default
|
||||||
"""
|
|
||||||
|
elif self.state_mod == 1: # all top/bottom
|
||||||
|
if edge == "right":
|
||||||
|
vertical = False
|
||||||
|
fake_center = vector(c.x, ll.y - self.track_wire * 2)
|
||||||
|
self.distance_right += 1
|
||||||
|
else:
|
||||||
|
if edge == "left":
|
||||||
|
vertical = False
|
||||||
|
fake_center = vector(c.x, ll.y + self.track_wire * 2)
|
||||||
|
self.distance_left += 1
|
||||||
|
|
||||||
|
elif self.state_mod == 2: # all left/right
|
||||||
|
if (edge == "bottom") or (edge == "right"):# change to the east
|
||||||
|
vertical = True
|
||||||
|
fake_center = vector(ur.x + self.track_wire * 2, ll.y + 30 + self.distance_right)
|
||||||
|
self.distance_right += 1
|
||||||
|
else:
|
||||||
|
if (edge == "top") or (edge == "left"):# change to the west
|
||||||
|
vertical = True
|
||||||
|
fake_center = vector(ll.x - self.track_wire * 2, ur.y - 30 - self.distance_left)
|
||||||
|
self.distance_left += 1
|
||||||
|
else:
|
||||||
|
debug.error("wrong state mod!", -1)
|
||||||
|
|
||||||
# Create the fake pin shape
|
# Create the fake pin shape
|
||||||
layer = self.get_layer(int(not vertical))
|
layer = self.get_layer(int(not vertical))
|
||||||
half_wire_vector = vector([self.half_wire] * 2)
|
half_wire_vector = vector([self.half_wire] * 2)
|
||||||
nll = fake_center - half_wire_vector
|
nll = fake_center - half_wire_vector
|
||||||
nur = fake_center + half_wire_vector
|
nur = fake_center + half_wire_vector
|
||||||
#not test jet
|
|
||||||
#half_wire_vector = vector([self.half_wire] * 2)# out *2 means vector([self.half_wire, self.half_wire])
|
|
||||||
#nll = fake_center - half_wire_vector - half_wire_vector
|
|
||||||
#nur = fake_center + half_wire_vector + half_wire_vector
|
|
||||||
rect = [nll, nur]
|
rect = [nll, nur]
|
||||||
pin = graph_shape(name="fake",
|
pin = graph_shape(name="fake",
|
||||||
rect=rect,
|
rect=rect,
|
||||||
layer_name_pp=layer)
|
layer_name_pp=layer)
|
||||||
print("this create_fake_pin")
|
|
||||||
print(pin.name)
|
|
||||||
print(pin.center)
|
|
||||||
return pin
|
return pin
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -234,8 +257,6 @@ class signal_escape_router(router):
|
||||||
|
|
||||||
to_route = []
|
to_route = []
|
||||||
for name in pin_names:
|
for name in pin_names:
|
||||||
print("==============the pin names===================")
|
|
||||||
print(name)
|
|
||||||
pin = next(iter(self.pins[name]))
|
pin = next(iter(self.pins[name]))
|
||||||
fake = self.create_fake_pin(pin)
|
fake = self.create_fake_pin(pin)
|
||||||
to_route.append((pin, fake, pin.distance(fake)))
|
to_route.append((pin, fake, pin.distance(fake)))
|
||||||
|
|
|
||||||
|
|
@ -1,311 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
from openram import debug
|
|
||||||
from openram.base.vector import vector
|
|
||||||
from openram.base.vector3d import vector3d
|
|
||||||
from openram import OPTS
|
|
||||||
from .graph import graph
|
|
||||||
from .graph_shape import graph_shape
|
|
||||||
from .router import router
|
|
||||||
import re
|
|
||||||
|
|
||||||
class signal_escape_router_change(router):
|
|
||||||
"""
|
|
||||||
This is the signal escape router that uses the Hanan grid graph method.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, layers, design, bbox=None, mod=0):
|
|
||||||
|
|
||||||
# `router` is the base router class
|
|
||||||
router.__init__(self, layers, design, bbox)
|
|
||||||
|
|
||||||
# New pins are the side supply pins
|
|
||||||
self.new_pins = {}
|
|
||||||
|
|
||||||
# Use for add distance of dout pins at the perimeter
|
|
||||||
self.distance_right = 0
|
|
||||||
|
|
||||||
self.distance_left = 0
|
|
||||||
|
|
||||||
# Use for control which edge/position the pins(dout) will be placed
|
|
||||||
# 0 -> default
|
|
||||||
# 1 -> all top/bottom
|
|
||||||
# 2 -> all left/right
|
|
||||||
self.state_mod = mod
|
|
||||||
|
|
||||||
|
|
||||||
def route(self, pin_names):
|
|
||||||
""" Route the given pins to the perimeter. """
|
|
||||||
debug.info(1, "Running signal escape router...")
|
|
||||||
|
|
||||||
# Prepare gdsMill to find pins and blockages
|
|
||||||
self.prepare_gds_reader()
|
|
||||||
|
|
||||||
# Find pins to be routed
|
|
||||||
for name in pin_names:
|
|
||||||
self.find_pins(name)
|
|
||||||
|
|
||||||
# Find blockages and vias
|
|
||||||
self.find_blockages()
|
|
||||||
self.find_vias()
|
|
||||||
|
|
||||||
# Convert blockages and vias if they overlap a pin
|
|
||||||
self.convert_vias()
|
|
||||||
self.convert_blockages()
|
|
||||||
|
|
||||||
# Add fake pins on the perimeter to do the escape routing on
|
|
||||||
self.add_perimeter_fake_pins()
|
|
||||||
|
|
||||||
# Add vdd and gnd pins as blockages as well
|
|
||||||
# NOTE: This is done to make vdd and gnd pins DRC-safe
|
|
||||||
for pin in self.all_pins:
|
|
||||||
self.blockages.append(self.inflate_shape(pin))
|
|
||||||
|
|
||||||
# Route vdd and gnd
|
|
||||||
routed_count = 0
|
|
||||||
routed_max = len(pin_names)
|
|
||||||
for source, target, _ in self.get_route_pairs(pin_names):
|
|
||||||
# Change fake pin's name so the graph will treat it as routable
|
|
||||||
target.name = source.name
|
|
||||||
# Create the graph
|
|
||||||
g = graph(self)
|
|
||||||
g.create_graph(source, target)
|
|
||||||
# Find the shortest path from source to target
|
|
||||||
path = g.find_shortest_path()
|
|
||||||
# If no path is found, throw an error
|
|
||||||
if path is None:
|
|
||||||
self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target)
|
|
||||||
debug.error("Couldn't route from {} to {}.".format(source, target), -1)
|
|
||||||
# Create the path shapes on layout
|
|
||||||
new_wires, new_vias = self.add_path(path)
|
|
||||||
self.new_pins[source.name] = new_wires[-1]
|
|
||||||
# Find the recently added shapes
|
|
||||||
self.find_blockages(name, new_wires)
|
|
||||||
self.find_vias(new_vias)
|
|
||||||
routed_count += 1
|
|
||||||
debug.info(2, "Routed {} of {} signal pins".format(routed_count, routed_max))
|
|
||||||
print("route pins:")
|
|
||||||
print(source)
|
|
||||||
self.replace_layout_pins()
|
|
||||||
|
|
||||||
|
|
||||||
def get_closest_edge(self, point):
|
|
||||||
""" Return a point's the closest edge and the edge's axis direction. """
|
|
||||||
|
|
||||||
ll, ur = self.bbox
|
|
||||||
|
|
||||||
# Snap the pin to the perimeter and break the iteration
|
|
||||||
ll_diff_x = abs(point.x - ll.x)
|
|
||||||
ll_diff_y = abs(point.y - ll.y)
|
|
||||||
ur_diff_x = abs(point.x - ur.x)
|
|
||||||
ur_diff_y = abs(point.y - ur.y)
|
|
||||||
min_diff = min(ll_diff_x, ll_diff_y, ur_diff_x, ur_diff_y)
|
|
||||||
|
|
||||||
if min_diff == ll_diff_x:
|
|
||||||
return "left", True
|
|
||||||
if min_diff == ll_diff_y:
|
|
||||||
return "bottom", False
|
|
||||||
if min_diff == ur_diff_x:
|
|
||||||
return "right", True
|
|
||||||
return "top", False
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_path(self, path):
|
|
||||||
"""
|
|
||||||
Override the `prepare_path` method from the `router` class to prevent
|
|
||||||
overflows from the SRAM layout area.
|
|
||||||
"""
|
|
||||||
|
|
||||||
ll, ur = self.bbox
|
|
||||||
nodes = super().prepare_path(path)
|
|
||||||
new_nodes = []
|
|
||||||
for i in range(len(nodes)):
|
|
||||||
node = nodes[i]
|
|
||||||
c = node.center
|
|
||||||
# Haven't overflown yet
|
|
||||||
if ll.x < c.x and c.x < ur.x and ll.y < c.y and c.y < ur.y:
|
|
||||||
new_nodes.append(node)
|
|
||||||
continue
|
|
||||||
# Snap the pin to the perimeter and break the iteration
|
|
||||||
edge, _ = self.get_closest_edge(c)
|
|
||||||
if edge == "left":
|
|
||||||
fake_center = vector3d(ll.x + self.half_wire, c.y, c.z)
|
|
||||||
if edge == "bottom":
|
|
||||||
fake_center = vector3d(c.x, ll.y + self.half_wire, c.z)
|
|
||||||
if edge == "right":
|
|
||||||
fake_center = vector3d(ur.x - self.half_wire, c.y, c.z)
|
|
||||||
if edge == "top":
|
|
||||||
fake_center = vector3d(c.x, ur.y - self.half_wire, c.z)
|
|
||||||
node.center = fake_center
|
|
||||||
new_nodes.append(node)
|
|
||||||
break
|
|
||||||
return new_nodes
|
|
||||||
|
|
||||||
|
|
||||||
def add_perimeter_fake_pins(self):
|
|
||||||
"""
|
|
||||||
Add the fake pins on the perimeter to where the signals will be routed.
|
|
||||||
These perimeter fake pins are only used to replace layout pins at the
|
|
||||||
end of routing.
|
|
||||||
"""
|
|
||||||
|
|
||||||
ll, ur = self.bbox
|
|
||||||
wide = self.track_wire
|
|
||||||
|
|
||||||
for side in ["top", "bottom", "left", "right"]:
|
|
||||||
vertical = side in ["left", "right"]
|
|
||||||
|
|
||||||
# Calculate the lower left coordinate
|
|
||||||
if side == "top":
|
|
||||||
offset = vector(ll.x, ur.y - wide)
|
|
||||||
elif side == "bottom":
|
|
||||||
offset = vector(ll.x, ll.y)
|
|
||||||
elif side == "left":
|
|
||||||
offset = vector(ll.x, ll.y)
|
|
||||||
elif side == "right":
|
|
||||||
offset = vector(ur.x - wide, ll.y)
|
|
||||||
|
|
||||||
# Calculate width and height
|
|
||||||
shape = ur - ll
|
|
||||||
if vertical:
|
|
||||||
shape_width = wide
|
|
||||||
shape_height = shape.y
|
|
||||||
else:
|
|
||||||
shape_width = shape.x
|
|
||||||
shape_height = wide
|
|
||||||
|
|
||||||
# Add this new pin
|
|
||||||
# They must lie on the non-preferred direction since the side supply
|
|
||||||
# pins will lie on the preferred direction
|
|
||||||
layer = self.get_layer(int(not vertical))
|
|
||||||
nll = vector(offset.x, offset.y)
|
|
||||||
nur = vector(offset.x + shape_width, offset.y + shape_height)
|
|
||||||
rect = [nll, nur]
|
|
||||||
pin = graph_shape(name="fake",
|
|
||||||
rect=rect,
|
|
||||||
layer_name_pp=layer)
|
|
||||||
self.fake_pins.append(pin)
|
|
||||||
print("this is add_per")
|
|
||||||
print(pin.name)
|
|
||||||
print(pin.center)
|
|
||||||
|
|
||||||
def create_fake_pin(self, pin):
|
|
||||||
""" Create a fake pin on the perimeter orthogonal to the given pin. """
|
|
||||||
|
|
||||||
ll, ur = self.bbox
|
|
||||||
c = pin.center()
|
|
||||||
print("inside pin name")
|
|
||||||
print("----------------------------------------------------------")
|
|
||||||
print(pin.name)
|
|
||||||
# Find the closest edge
|
|
||||||
edge, vertical = self.get_closest_edge(c)
|
|
||||||
|
|
||||||
# Keep the fake pin out of the SRAM layout are so that they won't be
|
|
||||||
# blocked by previous signals if they're on the same orthogonal line
|
|
||||||
if edge == "left":
|
|
||||||
fake_center = vector(ll.x - self.track_wire * 2, c.y)
|
|
||||||
if edge == "bottom":
|
|
||||||
fake_center = vector(c.x, ll.y - self.track_wire * 2)
|
|
||||||
if edge == "right":
|
|
||||||
fake_center = vector(ur.x + self.track_wire * 2, c.y)
|
|
||||||
if edge == "top":
|
|
||||||
fake_center = vector(c.x, ur.y + self.track_wire * 2)
|
|
||||||
#fake_center = vector(ll.x - self.track_wire * 2, c.y) # test if here we could change the pin position at the layout
|
|
||||||
|
|
||||||
# relocate the pin position
|
|
||||||
pattern = r'^dout'
|
|
||||||
if re.match(pattern, pin.name):
|
|
||||||
|
|
||||||
if self.state_mod == 0:
|
|
||||||
pass# do not change, default
|
|
||||||
|
|
||||||
elif self.state_mod == 1: # all top/bottom
|
|
||||||
if edge == "right":
|
|
||||||
vertical = False
|
|
||||||
fake_center = vector(c.x, ll.y - self.track_wire * 2)
|
|
||||||
self.distance_right += 1
|
|
||||||
else:
|
|
||||||
if edge == "left":
|
|
||||||
vertical = False
|
|
||||||
fake_center = vector(c.x, ll.y + self.track_wire * 2)
|
|
||||||
self.distance_left += 1
|
|
||||||
|
|
||||||
elif self.state_mod == 2: # all left/right
|
|
||||||
if (edge == "bottom") or (edge == "right"):# change to the east
|
|
||||||
vertical = True
|
|
||||||
fake_center = vector(ur.x + self.track_wire * 2, ll.y + 30 + self.distance_right)
|
|
||||||
self.distance_right += 1
|
|
||||||
else:
|
|
||||||
if (edge == "top") or (edge == "left"):# change to the west
|
|
||||||
vertical = True
|
|
||||||
fake_center = vector(ll.x - self.track_wire * 2, ur.y - 30 - self.distance_left)
|
|
||||||
self.distance_left += 1
|
|
||||||
else:
|
|
||||||
debug.error("wrong state mod!", -1)
|
|
||||||
''' # old version
|
|
||||||
if edge == "bottom":# change to the east
|
|
||||||
vertical = True
|
|
||||||
fake_center = vector(ur.x + self.track_wire * 2, ll.y + 30 + self.distance_right)
|
|
||||||
self.distance_right += 1
|
|
||||||
else:
|
|
||||||
if edge == "top":# change to the west
|
|
||||||
vertical = True
|
|
||||||
fake_center = vector(ll.x - self.track_wire * 2, ur.y - 30 - self.distance_left)
|
|
||||||
self.distance_left += 1
|
|
||||||
'''
|
|
||||||
"""
|
|
||||||
pattern = r'^addr0_1'
|
|
||||||
if re.match(pattern, pin.name):
|
|
||||||
vertical = True
|
|
||||||
fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_wire * 4)# fix still do not know how to control the distance between every fake pin
|
|
||||||
#do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y?
|
|
||||||
"""
|
|
||||||
# Create the fake pin shape
|
|
||||||
layer = self.get_layer(int(not vertical))
|
|
||||||
half_wire_vector = vector([self.half_wire] * 2)
|
|
||||||
nll = fake_center - half_wire_vector
|
|
||||||
nur = fake_center + half_wire_vector
|
|
||||||
#not test jet
|
|
||||||
#half_wire_vector = vector([self.half_wire] * 2)# out *2 means vector([self.half_wire, self.half_wire])
|
|
||||||
#nll = fake_center - half_wire_vector - half_wire_vector
|
|
||||||
#nur = fake_center + half_wire_vector + half_wire_vector
|
|
||||||
rect = [nll, nur]
|
|
||||||
pin = graph_shape(name="fake",
|
|
||||||
rect=rect,
|
|
||||||
layer_name_pp=layer)
|
|
||||||
print("this create_fake_pin")
|
|
||||||
print(pin.name)
|
|
||||||
print(pin.center)
|
|
||||||
return pin
|
|
||||||
|
|
||||||
|
|
||||||
def get_route_pairs(self, pin_names):
|
|
||||||
""" Return the pairs to be routed. """
|
|
||||||
|
|
||||||
to_route = []
|
|
||||||
for name in pin_names:
|
|
||||||
print("==============the pin names===================")
|
|
||||||
print(name)
|
|
||||||
pin = next(iter(self.pins[name]))
|
|
||||||
fake = self.create_fake_pin(pin)
|
|
||||||
to_route.append((pin, fake, pin.distance(fake)))
|
|
||||||
return sorted(to_route, key=lambda x: x[2])
|
|
||||||
|
|
||||||
|
|
||||||
def replace_layout_pins(self):
|
|
||||||
""" Replace the old layout pins with new ones around the perimeter. """
|
|
||||||
|
|
||||||
for name, pin in self.new_pins.items():
|
|
||||||
print("name:{0}".format(name))
|
|
||||||
print("replace_pin->{0}".format(pin))
|
|
||||||
pin = graph_shape(pin.name, pin.boundary, pin.lpp)
|
|
||||||
# Find the intersection of this pin on the perimeter
|
|
||||||
for fake in self.fake_pins:
|
|
||||||
edge = pin.intersection(fake)
|
|
||||||
if edge:
|
|
||||||
break
|
|
||||||
self.design.replace_layout_pin(name, edge)
|
|
||||||
print("pass!!!!!!!")
|
|
||||||
|
|
@ -76,8 +76,6 @@ class supply_router(router):
|
||||||
# Create the graph
|
# Create the graph
|
||||||
g = graph(self)
|
g = graph(self)
|
||||||
g.create_graph(source, target)
|
g.create_graph(source, target)
|
||||||
# debug
|
|
||||||
debug.warning("graph creat success!")
|
|
||||||
# Find the shortest path from source to target
|
# Find the shortest path from source to target
|
||||||
path = g.find_shortest_path()
|
path = g.find_shortest_path()
|
||||||
# If no path is found, throw an error
|
# If no path is found, throw an error
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,64 @@ class sram():
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
from openram.modules.sram_1bank import sram_1bank as sram
|
from openram.modules.sram_1bank import sram_1bank as sram
|
||||||
#from openram.modules.sram_new import sram_1bank as sram
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
|
|
||||||
self.s.create_netlist()
|
num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports
|
||||||
|
self.s = sram(name, sram_config)
|
||||||
|
self.s.create_netlist()# not placed & routed jet
|
||||||
|
cur_state = "IDLE"
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.s.create_layout()
|
i = 0
|
||||||
|
while i < (OPTS.word_size + 100):
|
||||||
|
|
||||||
|
debug.warning("current state: state = {0}".format(cur_state))
|
||||||
|
|
||||||
|
if cur_state == "IDLE":# default, fisrt try
|
||||||
|
try:
|
||||||
|
self.s.create_layout(position_add=i)
|
||||||
|
except AssertionError as e:
|
||||||
|
cur_state = "ALL_TOP_BOTTOM"
|
||||||
|
del self.s
|
||||||
|
self.s = sram(name, sram_config)
|
||||||
|
self.s.create_netlist()
|
||||||
|
if num_ports > 1:
|
||||||
|
i = 16
|
||||||
|
else:
|
||||||
|
i = 0
|
||||||
|
continue
|
||||||
|
cur_state = "FINISH"
|
||||||
|
break
|
||||||
|
|
||||||
|
elif cur_state == "ALL_TOP_BOTTOM":
|
||||||
|
try:
|
||||||
|
self.s.create_layout(position_add=i, mod=1)
|
||||||
|
except AssertionError as e:
|
||||||
|
cur_state = "ALL_LEFT_RIGHT"
|
||||||
|
i = OPTS.word_size + 24
|
||||||
|
del self.s
|
||||||
|
self.s = sram(name, sram_config)
|
||||||
|
self.s.create_netlist()
|
||||||
|
continue
|
||||||
|
cur_state = "FINISH"
|
||||||
|
break
|
||||||
|
|
||||||
|
elif cur_state == "ALL_LEFT_RIGHT":
|
||||||
|
try:
|
||||||
|
self.s.create_layout(position_add=i, mod=2)
|
||||||
|
except AssertionError as e:
|
||||||
|
cur_state = "ALL_LEFT_RIGHT"
|
||||||
|
i = i + 1
|
||||||
|
if i == (99 + OPTS.word_size):# failed in rounting
|
||||||
|
debug.error("Failed in rounting", -1)
|
||||||
|
break
|
||||||
|
del self.s
|
||||||
|
self.s = sram(name, sram_config)
|
||||||
|
self.s.create_netlist()
|
||||||
|
continue
|
||||||
|
cur_state = "FINISH"
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
cur_state = "FINISH"
|
||||||
|
break
|
||||||
|
|
||||||
if not OPTS.is_unit_test:
|
if not OPTS.is_unit_test:
|
||||||
print_time("SRAM creation", datetime.datetime.now(), start_time)
|
print_time("SRAM creation", datetime.datetime.now(), start_time)
|
||||||
|
|
@ -110,7 +162,7 @@ class sram():
|
||||||
spname = OPTS.output_path + self.s.name + ".sp"
|
spname = OPTS.output_path + self.s.name + ".sp"
|
||||||
debug.print_raw("SP: Writing to {0}".format(spname))
|
debug.print_raw("SP: Writing to {0}".format(spname))
|
||||||
self.sp_write(spname)
|
self.sp_write(spname)
|
||||||
|
#''' #comment the following state when generating big sram, and then disable drc/lvs, because maigc_ext stuck
|
||||||
# Save a functional simulation file with default period
|
# Save a functional simulation file with default period
|
||||||
functional(self.s,
|
functional(self.s,
|
||||||
spname,
|
spname,
|
||||||
|
|
@ -132,7 +184,7 @@ class sram():
|
||||||
d.targ_write_ports = [self.s.write_ports[0]]
|
d.targ_write_ports = [self.s.write_ports[0]]
|
||||||
d.write_delay_stimulus()
|
d.write_delay_stimulus()
|
||||||
print_time("DELAY", datetime.datetime.now(), start_time)
|
print_time("DELAY", datetime.datetime.now(), start_time)
|
||||||
|
#''' #comment the above when generating big sram, and then disable drc/;vs, bevause magic_ext stuck
|
||||||
# Save trimmed spice file
|
# Save trimmed spice file
|
||||||
temp_trim_sp = "{0}trimmed.sp".format(OPTS.output_path)
|
temp_trim_sp = "{0}trimmed.sp".format(OPTS.output_path)
|
||||||
self.sp_write(temp_trim_sp, lvs=False, trim=True)
|
self.sp_write(temp_trim_sp, lvs=False, trim=True)
|
||||||
|
|
|
||||||
|
|
@ -1,338 +0,0 @@
|
||||||
# See LICENSE for licensing information.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016-2024 Regents of the University of California and The Board
|
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import datetime
|
|
||||||
import copy
|
|
||||||
from openram import debug
|
|
||||||
from openram import sram_config as config
|
|
||||||
from openram import OPTS, print_time
|
|
||||||
|
|
||||||
|
|
||||||
class sram():
|
|
||||||
"""
|
|
||||||
This is not a design module, but contains an SRAM design instance.
|
|
||||||
It could later try options of number of banks and organization to compare
|
|
||||||
results.
|
|
||||||
We can later add visualizer and other high-level functions as needed.
|
|
||||||
"""
|
|
||||||
def __init__(self, sram_config=None, name=None):
|
|
||||||
|
|
||||||
# Create default configs if custom config isn't provided
|
|
||||||
if sram_config is None:
|
|
||||||
sram_config = config(word_size=OPTS.word_size,
|
|
||||||
num_words=OPTS.num_words,
|
|
||||||
write_size=OPTS.write_size,
|
|
||||||
num_banks=OPTS.num_banks,
|
|
||||||
words_per_row=OPTS.words_per_row,
|
|
||||||
num_spare_rows=OPTS.num_spare_rows,
|
|
||||||
num_spare_cols=OPTS.num_spare_cols)
|
|
||||||
|
|
||||||
if name is None:
|
|
||||||
name = OPTS.output_name
|
|
||||||
|
|
||||||
sram_config.set_local_config(self)
|
|
||||||
|
|
||||||
# reset the static duplicate name checker for unit tests
|
|
||||||
# in case we create more than one SRAM
|
|
||||||
from openram.base import design
|
|
||||||
design.name_map=[]
|
|
||||||
|
|
||||||
debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size,
|
|
||||||
self.num_words,
|
|
||||||
self.num_banks))
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
from openram.modules.sram_new import sram_1bank as sram
|
|
||||||
|
|
||||||
num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
self.s.create_netlist()# not placed & routed jet
|
|
||||||
cur_state = "IDLE"
|
|
||||||
if not OPTS.netlist_only:
|
|
||||||
i = 0
|
|
||||||
while i < (OPTS.word_size + 100):
|
|
||||||
|
|
||||||
print("current iteration: i = {0}".format(i))
|
|
||||||
debug.warning("current state: state = {0}".format(cur_state))
|
|
||||||
|
|
||||||
if cur_state == "IDLE":# default, fisrt try
|
|
||||||
try:
|
|
||||||
self.s.create_layout_recrusive(position_add=i)
|
|
||||||
except AssertionError as e:
|
|
||||||
cur_state = "ALL_TOP_BOTTOM"
|
|
||||||
del self.s
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
self.s.create_netlist()
|
|
||||||
if num_ports > 1:
|
|
||||||
i = 16
|
|
||||||
else:
|
|
||||||
i = 0
|
|
||||||
continue
|
|
||||||
cur_state = "FINISH"
|
|
||||||
break
|
|
||||||
|
|
||||||
elif cur_state == "ALL_TOP_BOTTOM":
|
|
||||||
try:
|
|
||||||
self.s.create_layout_recrusive(position_add=i, mod=1)
|
|
||||||
except AssertionError as e:
|
|
||||||
cur_state = "ALL_LEFT_RIGHT"
|
|
||||||
i = OPTS.word_size + 24
|
|
||||||
del self.s
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
self.s.create_netlist()
|
|
||||||
continue
|
|
||||||
cur_state = "FINISH"
|
|
||||||
break
|
|
||||||
|
|
||||||
elif cur_state == "ALL_LEFT_RIGHT":
|
|
||||||
try:
|
|
||||||
self.s.create_layout_recrusive(position_add=i, mod=2)
|
|
||||||
except AssertionError as e:
|
|
||||||
cur_state = "ALL_LEFT_RIGHT"
|
|
||||||
i = i + 1
|
|
||||||
if i == (99 + OPTS.word_size):# failed in rounting
|
|
||||||
debug.error("Failed in rounting", -1)
|
|
||||||
break
|
|
||||||
del self.s
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
self.s.create_netlist()
|
|
||||||
continue
|
|
||||||
cur_state = "FINISH"
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
cur_state = "FINISH"
|
|
||||||
break
|
|
||||||
|
|
||||||
''' # 2nd version
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
self.s.create_netlist()# not placed & routed jet
|
|
||||||
if not OPTS.netlist_only:
|
|
||||||
i = 0
|
|
||||||
supply_route_not_found = False
|
|
||||||
while i < (OPTS.word_size + 100):
|
|
||||||
print("current iteration: i = {0}".format(i))
|
|
||||||
try:
|
|
||||||
self.s.create_layout_recrusive(position_add=i)
|
|
||||||
except AssertionError as e:
|
|
||||||
supply_route_not_found = True
|
|
||||||
if i == (99 + OPTS.word_size):# failed in rounting
|
|
||||||
debug.error("Failed in rounting", -1)
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
if (supply_route_not_found):
|
|
||||||
del self.s
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
self.s.create_netlist()
|
|
||||||
if i == 0:# after first try
|
|
||||||
i = OPTS.word_size + 20
|
|
||||||
else:
|
|
||||||
i = i + 1
|
|
||||||
supply_route_not_found = False
|
|
||||||
else:# successfully routed
|
|
||||||
break
|
|
||||||
'''
|
|
||||||
'''#old version
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
self.s.create_netlist()# not placed & routed jet
|
|
||||||
#self.s_tmp = copy.deepcopy(self.s) # need deepcopy
|
|
||||||
if not OPTS.netlist_only:
|
|
||||||
i = 98
|
|
||||||
supply_route_not_found = False
|
|
||||||
#self.s_tmp = copy.deepcopy(self.s) # need deepcopy
|
|
||||||
while i < 100:
|
|
||||||
print("current iteration: {0}".format(i))
|
|
||||||
try:
|
|
||||||
self.s.create_layout_recrusive(position_add=i)
|
|
||||||
except AssertionError as e:
|
|
||||||
supply_route_not_found = True
|
|
||||||
if i == 99:# failed in rounting
|
|
||||||
debug.error("Failed in rounting", -1)
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
if (supply_route_not_found):
|
|
||||||
#self.s_tmp = copy.deepcopy(self.s) # need deepcopy
|
|
||||||
del self.s
|
|
||||||
self.s = sram(name, sram_config)
|
|
||||||
self.s.create_netlist()
|
|
||||||
i = i + 1
|
|
||||||
supply_route_not_found = False
|
|
||||||
else:# successfully routed
|
|
||||||
#self.s = copy.deepcopy(self.s_tmp) # need deepcopy
|
|
||||||
break
|
|
||||||
'''
|
|
||||||
if not OPTS.is_unit_test:
|
|
||||||
print_time("SRAM creation", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
def get_sp_name(self):
|
|
||||||
if OPTS.use_pex:
|
|
||||||
# Use the extracted spice file
|
|
||||||
return self.pex_name
|
|
||||||
else:
|
|
||||||
# Use generated spice file for characterization
|
|
||||||
return self.sp_name
|
|
||||||
|
|
||||||
def sp_write(self, name, lvs=False, trim=False):
|
|
||||||
self.s.sp_write(name, lvs, trim)
|
|
||||||
|
|
||||||
def lef_write(self, name):
|
|
||||||
self.s.lef_write(name)
|
|
||||||
|
|
||||||
def gds_write(self, name):
|
|
||||||
self.s.gds_write(name)
|
|
||||||
|
|
||||||
def verilog_write(self, name):
|
|
||||||
self.s.verilog_write(name)
|
|
||||||
if self.num_banks != 1:
|
|
||||||
from openram.modules.sram_multibank import sram_multibank
|
|
||||||
mb = sram_multibank(self.s)
|
|
||||||
mb.verilog_write(name[:-2] + '_top.v')
|
|
||||||
|
|
||||||
def extended_config_write(self, name):
|
|
||||||
"""Dump config file with all options.
|
|
||||||
Include defaults and anything changed by input config."""
|
|
||||||
f = open(name, "w")
|
|
||||||
var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name)))
|
|
||||||
for var_name, var_value in var_dict.items():
|
|
||||||
if isinstance(var_value, str):
|
|
||||||
f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n")
|
|
||||||
else:
|
|
||||||
f.write(str(var_name) + " = " + str(var_value)+ "\n")
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
""" Save all the output files while reporting time to do it as well. """
|
|
||||||
|
|
||||||
# Import this at the last minute so that the proper tech file
|
|
||||||
# is loaded and the right tools are selected
|
|
||||||
from openram import verify
|
|
||||||
from openram.characterizer import functional
|
|
||||||
from openram.characterizer import delay
|
|
||||||
|
|
||||||
# Save the spice file
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
spname = OPTS.output_path + self.s.name + ".sp"
|
|
||||||
debug.print_raw("SP: Writing to {0}".format(spname))
|
|
||||||
self.sp_write(spname)
|
|
||||||
'''
|
|
||||||
# Save a functional simulation file with default period
|
|
||||||
functional(self.s,
|
|
||||||
spname,
|
|
||||||
cycles=200,
|
|
||||||
output_path=OPTS.output_path)
|
|
||||||
print_time("Spice writing", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Save stimulus and measurement file
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
debug.print_raw("DELAY: Writing stimulus...")
|
|
||||||
d = delay(self.s, spname, ("TT", 5, 25), output_path=OPTS.output_path)
|
|
||||||
if (self.s.num_spare_rows == 0):
|
|
||||||
probe_address = "1" * self.s.addr_size
|
|
||||||
else:
|
|
||||||
probe_address = "0" + "1" * (self.s.addr_size - 1)
|
|
||||||
probe_data = self.s.word_size - 1
|
|
||||||
d.analysis_init(probe_address, probe_data)
|
|
||||||
d.targ_read_ports.extend(self.s.read_ports)
|
|
||||||
d.targ_write_ports = [self.s.write_ports[0]]
|
|
||||||
d.write_delay_stimulus()
|
|
||||||
print_time("DELAY", datetime.datetime.now(), start_time)
|
|
||||||
'''
|
|
||||||
# Save trimmed spice file
|
|
||||||
temp_trim_sp = "{0}trimmed.sp".format(OPTS.output_path)
|
|
||||||
self.sp_write(temp_trim_sp, lvs=False, trim=True)
|
|
||||||
|
|
||||||
if not OPTS.netlist_only:
|
|
||||||
# Write the layout
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
gdsname = OPTS.output_path + self.s.name + ".gds"
|
|
||||||
debug.print_raw("GDS: Writing to {0}".format(gdsname))
|
|
||||||
self.gds_write(gdsname)
|
|
||||||
if OPTS.check_lvsdrc:
|
|
||||||
verify.write_drc_script(cell_name=self.s.name,
|
|
||||||
gds_name=os.path.basename(gdsname),
|
|
||||||
extract=True,
|
|
||||||
final_verification=True,
|
|
||||||
output_path=OPTS.output_path)
|
|
||||||
print_time("GDS", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Create a LEF physical model
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
lefname = OPTS.output_path + self.s.name + ".lef"
|
|
||||||
debug.print_raw("LEF: Writing to {0}".format(lefname))
|
|
||||||
self.lef_write(lefname)
|
|
||||||
print_time("LEF", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Save the LVS file
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
|
|
||||||
debug.print_raw("LVS: Writing to {0}".format(lvsname))
|
|
||||||
self.sp_write(lvsname, lvs=True)
|
|
||||||
if not OPTS.netlist_only and OPTS.check_lvsdrc:
|
|
||||||
verify.write_lvs_script(cell_name=self.s.name,
|
|
||||||
gds_name=os.path.basename(gdsname),
|
|
||||||
sp_name=os.path.basename(lvsname),
|
|
||||||
final_verification=True,
|
|
||||||
output_path=OPTS.output_path)
|
|
||||||
print_time("LVS writing", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Save the extracted spice file
|
|
||||||
if OPTS.use_pex:
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
# Output the extracted design if requested
|
|
||||||
pexname = OPTS.output_path + self.s.name + ".pex.sp"
|
|
||||||
spname = OPTS.output_path + self.s.name + ".sp"
|
|
||||||
verify.run_pex(self.s.name, gdsname, spname, output=pexname)
|
|
||||||
sp_file = pexname
|
|
||||||
print_time("Extraction", datetime.datetime.now(), start_time)
|
|
||||||
else:
|
|
||||||
# Use generated spice file for characterization
|
|
||||||
sp_file = spname
|
|
||||||
|
|
||||||
# Characterize the design
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
from openram.characterizer import lib
|
|
||||||
debug.print_raw("LIB: Characterizing... ")
|
|
||||||
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
|
|
||||||
print_time("Characterization", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Write the config file
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
try:
|
|
||||||
from shutil import copyfile
|
|
||||||
copyfile(OPTS.config_file, OPTS.output_path + OPTS.output_name + '.py')
|
|
||||||
except shutil.SameFileError:
|
|
||||||
pass
|
|
||||||
debug.print_raw("Config: Writing to {0}".format(OPTS.output_path + OPTS.output_name + '.py'))
|
|
||||||
print_time("Config", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Write the datasheet
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
from openram.datasheet import datasheet_gen
|
|
||||||
dname = OPTS.output_path + self.s.name + ".html"
|
|
||||||
debug.print_raw("Datasheet: Writing to {0}".format(dname))
|
|
||||||
datasheet_gen.datasheet_write(dname)
|
|
||||||
print_time("Datasheet", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Write a verilog model
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
vname = OPTS.output_path + self.s.name + '.v'
|
|
||||||
debug.print_raw("Verilog: Writing to {0}".format(vname))
|
|
||||||
self.verilog_write(vname)
|
|
||||||
print_time("Verilog", datetime.datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Write out options if specified
|
|
||||||
if OPTS.output_extended_config:
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
oname = OPTS.output_path + OPTS.output_name + "_extended.py"
|
|
||||||
debug.print_raw("Extended Config: Writing to {0}".format(oname))
|
|
||||||
self.extended_config_write(oname)
|
|
||||||
print_time("Extended Config", datetime.datetime.now(), start_time)
|
|
||||||
|
|
@ -69,12 +69,13 @@ for path in output_files:
|
||||||
debug.print_raw(path)
|
debug.print_raw(path)
|
||||||
|
|
||||||
# Create an SRAM (we can also pass sram_config, see documentation/tutorials for details)
|
# Create an SRAM (we can also pass sram_config, see documentation/tutorials for details)
|
||||||
#from openram import sram
|
from openram import sram
|
||||||
#s = sram()
|
s = sram()
|
||||||
from openram import sram_new_test
|
#from openram import sram_openroad_test
|
||||||
s = sram_new_test.sram()
|
#s = sram_openroad_test.sram()
|
||||||
# Output the files for the resulting SRAM
|
# Output the files for the resulting SRAM
|
||||||
s.save()
|
s.save()
|
||||||
|
#s.save_only()
|
||||||
'''
|
'''
|
||||||
del s
|
del s
|
||||||
s = sram_road.sram(mod=1)
|
s = sram_road.sram(mod=1)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue