OpenRAM/compiler/router/router_tech.py

148 lines
6.1 KiB
Python

# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 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 math
from openram import debug
from openram.base.contact import contact
from openram.base.vector import vector
from openram.tech import drc, layer, preferred_directions
class router_tech:
"""
This is a class to hold the router tech constants.
"""
def __init__(self, layers, route_track_width):
"""
Allows us to change the layers that we are routing on.
This uses the preferreed directions.
"""
self.layers = layers
self.route_track_width = route_track_width
if len(self.layers) == 1:
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
else:
(try_horiz_layer, self.via_layer_name, try_vert_layer) = self.layers
# figure out wich of the two layers prefers horizontal/vertical
# routing
self.horiz_layer_name = None
self.vert_layer_name = None
if preferred_directions[try_horiz_layer] == "H":
self.horiz_layer_name = try_horiz_layer
else:
self.horiz_layer_name = try_vert_layer
if preferred_directions[try_vert_layer] == "V":
self.vert_layer_name = try_vert_layer
else:
self.vert_layer_name = try_horiz_layer
if not self.horiz_layer_name or not self.vert_layer_name:
raise ValueError("Layer '{}' and '{}' are using the wrong "
"preferred_directions '{}' and '{}'.")
via_connect = contact(self.layers, (1, 1))
max_via_size = max(via_connect.width, via_connect.height)
self.horiz_lpp = layer[self.horiz_layer_name]
self.vert_lpp = layer[self.vert_layer_name]
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
# For supplies, we will make the wire wider than the vias
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size)
# Update spacing for the new widths
max_width = max(self.vert_layer_minwidth, self.horiz_layer_minwidth)
self.vert_layer_spacing = self.get_layer_space(1, max_width)
self.horiz_layer_spacing = self.get_layer_space(0, max_width)
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
# We'll keep horizontal and vertical tracks the same for simplicity.
self.track_width = max(self.horiz_track_width, self.vert_track_width)
debug.info(1, "Minimum track width: {:.3f}".format(self.track_width))
self.track_space = max(self.horiz_layer_spacing, self.vert_layer_spacing)
debug.info(1, "Minimum track space: {:.3f}".format(self.track_space))
self.track_wire = self.track_width - self.track_space
debug.info(1, "Minimum track wire width: {:.3f}".format(self.track_wire))
self.track_widths = vector([self.track_width] * 2)
self.track_factor = vector([1/self.track_width] * 2)
debug.info(2, "Track factor: {}".format(self.track_factor))
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
self.layer_widths = [self.track_wire, 1, self.track_wire]
def same_lpp(self, lpp1, lpp2):
"""
Check if the layers and purposes are the same.
Ignore if purpose is a None.
"""
if lpp1[1] == None or lpp2[1] == None:
return lpp1[0] == lpp2[0]
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]
def get_zindex(self, lpp):
if self.same_lpp(lpp, self.horiz_lpp):
return 0
else:
return 1
def get_layer(self, zindex):
if zindex==1:
return self.vert_layer_name
elif zindex==0:
return self.horiz_layer_name
else:
debug.error("Invalid zindex {}".format(zindex), -1)
def get_lpp(self, zindex):
if zindex == 1:
return self.vert_lpp
elif zindex == 0:
return self.horiz_lpp
else:
debug.error("Invalid zindex {}".format(zindex), -1)
def get_layer_width_space(self, zindex):
"""
These are the width and spacing of a supply layer given a supply rail
of the given number of min wire widths.
"""
min_width = self.get_layer_width(zindex)
min_spacing = self.get_layer_space(zindex, min_width)
return (min_width, min_spacing)
def get_layer_width(self, zindex):
""" Return the minimum width of a layer. """
layer_name = self.get_layer(zindex)
min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
min_width = self.route_track_width * drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf)
return min_width
def get_layer_space(self, zindex, width=None):
""" Return the minimum spacing of a layer given wire width. """
if width is None:
width = self.get_layer_width(zindex)
layer_name = self.get_layer(zindex)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * width, math.inf)
return min_spacing