2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2021-01-22 20:23:28 +01:00
|
|
|
# Copyright (c) 2016-2021 Regents of the University of California and The Board
|
2019-06-14 17:43:41 +02:00
|
|
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
|
|
|
# (acting for and on behalf of Oklahoma State University)
|
|
|
|
|
# All rights reserved.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2016-11-08 18:57:35 +01:00
|
|
|
from tech import drc
|
2022-07-13 19:57:56 +02:00
|
|
|
from .wire_path import wire_path
|
2019-01-17 01:56:06 +01:00
|
|
|
from sram_factory import factory
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-15 18:48:42 +02:00
|
|
|
|
2019-01-26 00:07:56 +01:00
|
|
|
class wire(wire_path):
|
2020-04-15 18:48:42 +02:00
|
|
|
"""
|
2016-11-08 18:57:35 +01:00
|
|
|
Object metal wire; given the layer type
|
2020-04-15 18:48:42 +02:00
|
|
|
Add a wire of minimium metal width between a set of points.
|
2016-11-08 18:57:35 +01:00
|
|
|
The points should be rectilinear to control the bend points. If
|
|
|
|
|
not, it will always go down first.
|
|
|
|
|
The points are the center of the wire.
|
2020-04-15 18:48:42 +02:00
|
|
|
The layer stack is the vertical, contact/via, and horizontal layers, respectively.
|
2020-05-07 21:35:21 +02:00
|
|
|
The widen option will avoid via-to-via spacing problems for really short segments
|
|
|
|
|
(added as an option so we can disable it in bus connections)
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
2020-05-07 21:35:21 +02:00
|
|
|
def __init__(self, obj, layer_stack, position_list, widen_short_wires=True):
|
2017-08-07 19:24:45 +02:00
|
|
|
self.obj = obj
|
2016-11-08 18:57:35 +01:00
|
|
|
self.layer_stack = layer_stack
|
|
|
|
|
self.position_list = position_list
|
2020-05-07 21:35:21 +02:00
|
|
|
self.widen_short_wires = widen_short_wires
|
2016-11-08 18:57:35 +01:00
|
|
|
self.pins = [] # used for matching parm lengths
|
|
|
|
|
self.switch_pos_list = []
|
|
|
|
|
|
|
|
|
|
self.create_layout()
|
|
|
|
|
|
|
|
|
|
def create_layout(self):
|
|
|
|
|
self.setup_layers()
|
2017-10-07 00:30:15 +02:00
|
|
|
self.create_rectilinear()
|
2016-11-08 18:57:35 +01:00
|
|
|
self.create_vias()
|
|
|
|
|
self.create_rectangles()
|
2019-01-26 00:07:56 +01:00
|
|
|
# wires and wire_paths should not be offset to (0,0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def setup_layers(self):
|
2020-04-15 18:48:42 +02:00
|
|
|
|
2016-11-17 23:05:50 +01:00
|
|
|
(horiz_layer, via_layer, vert_layer) = self.layer_stack
|
2016-11-18 23:10:30 +01:00
|
|
|
self.via_layer_name = via_layer
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
self.vert_layer_name = vert_layer
|
2018-10-12 23:37:51 +02:00
|
|
|
self.vert_layer_width = drc("minwidth_{0}".format(vert_layer))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
self.horiz_layer_name = horiz_layer
|
2018-10-12 23:37:51 +02:00
|
|
|
self.horiz_layer_width = drc("minwidth_{0}".format(horiz_layer))
|
2019-01-17 01:56:06 +01:00
|
|
|
via_connect = factory.create(module_type="contact",
|
|
|
|
|
layer_stack=self.layer_stack,
|
|
|
|
|
dimensions=(1, 1))
|
2020-04-15 18:48:42 +02:00
|
|
|
|
|
|
|
|
# This is used for short connections to avoid via-to-via spacing errors
|
|
|
|
|
self.vert_layer_contact_width = max(via_connect.second_layer_width,
|
|
|
|
|
via_connect.first_layer_width)
|
|
|
|
|
self.horiz_layer_contact_width = max(via_connect.second_layer_height,
|
|
|
|
|
via_connect.first_layer_height)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-10-12 23:37:51 +02:00
|
|
|
self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width,
|
|
|
|
|
drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height]
|
2020-04-15 20:16:45 +02:00
|
|
|
self.pitch = self.compute_pitch(self.layer_stack)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-15 20:16:45 +02:00
|
|
|
def compute_pitch(self, layer_stack):
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-15 20:16:45 +02:00
|
|
|
"""
|
|
|
|
|
This is contact direction independent pitch,
|
|
|
|
|
i.e. we take the maximum contact dimension
|
|
|
|
|
"""
|
2022-07-13 19:57:56 +02:00
|
|
|
|
|
|
|
|
# This is here for the unit tests which may not have
|
|
|
|
|
# initialized the static parts of the layout class yet.
|
|
|
|
|
from base import layout
|
|
|
|
|
layout("fake", "fake")
|
|
|
|
|
|
2020-04-15 20:16:45 +02:00
|
|
|
(layer1, via, layer2) = layer_stack
|
|
|
|
|
|
|
|
|
|
if layer1 == "poly" or layer1 == "active":
|
2022-07-13 19:57:56 +02:00
|
|
|
try:
|
|
|
|
|
contact1 = getattr(layout, layer1 + "_contact")
|
|
|
|
|
except AttributeError:
|
|
|
|
|
breakpoint()
|
2020-04-15 20:16:45 +02:00
|
|
|
else:
|
2020-04-16 01:49:04 +02:00
|
|
|
try:
|
2022-07-13 19:57:56 +02:00
|
|
|
contact1 = getattr(layout, layer1 + "_via")
|
2020-04-16 01:49:04 +02:00
|
|
|
except AttributeError:
|
2022-07-13 19:57:56 +02:00
|
|
|
contact1 = getattr(layout, layer2 + "_via")
|
2020-04-15 20:16:45 +02:00
|
|
|
max_contact = max(contact1.width, contact1.height)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-16 00:29:55 +02:00
|
|
|
layer1_space = drc("{0}_to_{0}".format(layer1))
|
|
|
|
|
layer2_space = drc("{0}_to_{0}".format(layer2))
|
2020-04-15 20:16:45 +02:00
|
|
|
pitch = max_contact + max(layer1_space, layer2_space)
|
|
|
|
|
|
|
|
|
|
return pitch
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
# create a 1x1 contact
|
|
|
|
|
def create_vias(self):
|
|
|
|
|
""" Add a via and corner square at every corner of the path."""
|
2019-01-17 01:56:06 +01:00
|
|
|
self.c=factory.create(module_type="contact",
|
|
|
|
|
layer_stack=self.layer_stack,
|
|
|
|
|
dimensions=(1, 1))
|
2020-04-15 18:48:42 +02:00
|
|
|
from itertools import tee, islice
|
|
|
|
|
nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
|
|
|
|
|
threewise = nwise(self.position_list, 3)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-07 19:24:45 +02:00
|
|
|
for (a, offset, c) in list(threewise):
|
|
|
|
|
# add a exceptions to prevent a via when we don't change directions
|
|
|
|
|
if a[0] == c[0]:
|
|
|
|
|
continue
|
|
|
|
|
if a[1] == c[1]:
|
|
|
|
|
continue
|
2019-04-01 23:23:47 +02:00
|
|
|
self.obj.add_via_center(layers=self.layer_stack,
|
|
|
|
|
offset=offset)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def create_rectangles(self):
|
2020-04-15 18:48:42 +02:00
|
|
|
"""
|
2019-04-01 23:23:47 +02:00
|
|
|
Create the actual rectangles on the appropriate layers
|
2020-04-15 18:48:42 +02:00
|
|
|
using the position list of the corners.
|
2019-04-01 23:23:47 +02:00
|
|
|
"""
|
2016-11-08 18:57:35 +01:00
|
|
|
pl = self.position_list # position list
|
|
|
|
|
for index in range(len(pl) - 1):
|
2020-04-15 18:48:42 +02:00
|
|
|
# Horizontal wire segment
|
2016-11-08 18:57:35 +01:00
|
|
|
if pl[index][0] != pl[index + 1][0]:
|
|
|
|
|
line_length = pl[index + 1][0] - pl[index][0]
|
2020-04-16 20:02:54 +02:00
|
|
|
# Make the wire wider to avoid via-to-via spacing problems
|
|
|
|
|
# But don't make it wider if it is shorter than one via
|
2020-05-07 21:35:21 +02:00
|
|
|
if self.widen_short_wires and abs(line_length) < self.pitch and abs(line_length) > self.horiz_layer_contact_width:
|
2020-04-15 18:48:42 +02:00
|
|
|
width = self.horiz_layer_contact_width
|
|
|
|
|
else:
|
|
|
|
|
width = self.horiz_layer_width
|
2017-08-07 19:24:45 +02:00
|
|
|
temp_offset = [pl[index][0],
|
2020-04-15 18:48:42 +02:00
|
|
|
pl[index][1] - 0.5 * width]
|
|
|
|
|
# If we go in the negative direction, move the offset
|
2016-11-08 18:57:35 +01:00
|
|
|
if line_length < 0:
|
|
|
|
|
temp_offset = [temp_offset[0] + line_length,
|
|
|
|
|
temp_offset[1]]
|
|
|
|
|
self.add_line(layer_name=self.horiz_layer_name,
|
|
|
|
|
length=abs(line_length),
|
|
|
|
|
offset=temp_offset,
|
2018-02-09 19:03:09 +01:00
|
|
|
orientation="horizontal",
|
2020-04-15 18:48:42 +02:00
|
|
|
layer_width=width)
|
|
|
|
|
# Vertical wire segment
|
2016-11-08 18:57:35 +01:00
|
|
|
elif pl[index][1] != pl[index + 1][1]:
|
|
|
|
|
line_length = pl[index + 1][1] - pl[index][1]
|
2020-04-16 20:02:54 +02:00
|
|
|
# Make the wire wider to avoid via-to-via spacing problems
|
|
|
|
|
# But don't make it wider if it is shorter than one via
|
2020-05-07 21:35:21 +02:00
|
|
|
if self.widen_short_wires and abs(line_length) < self.pitch and abs(line_length) > self.vert_layer_contact_width:
|
2020-04-15 18:48:42 +02:00
|
|
|
width = self.vert_layer_contact_width
|
|
|
|
|
else:
|
|
|
|
|
width = self.vert_layer_width
|
|
|
|
|
temp_offset = [pl[index][0] - 0.5 * width,
|
2017-08-07 19:24:45 +02:00
|
|
|
pl[index][1]]
|
2016-11-08 18:57:35 +01:00
|
|
|
if line_length < 0:
|
|
|
|
|
temp_offset = [temp_offset[0],
|
|
|
|
|
temp_offset[1] + line_length]
|
|
|
|
|
self.add_line(layer_name=self.vert_layer_name,
|
|
|
|
|
length=abs(line_length),
|
|
|
|
|
offset=temp_offset,
|
2018-02-09 19:03:09 +01:00
|
|
|
orientation="vertical",
|
2020-04-15 18:48:42 +02:00
|
|
|
layer_width=width)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def assert_node(self, A, B):
|
2020-04-15 18:48:42 +02:00
|
|
|
"""
|
|
|
|
|
Check if the node movements are not big enough for the
|
|
|
|
|
technology sizes.
|
|
|
|
|
"""
|
2016-11-08 18:57:35 +01:00
|
|
|
X_diff = abs(A[0] - B[0])
|
|
|
|
|
Y_diff = abs(A[1] - B[1])
|
|
|
|
|
[minX, minY] = self.node_to_node
|
|
|
|
|
if X_diff == 0 and Y_diff == 0:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
if X_diff == 0:
|
|
|
|
|
assert Y_diff >= minY, "node" + \
|
|
|
|
|
str(A) + " and node" + str(B) + \
|
|
|
|
|
" are too close in Y. Minmum is " + str(minX)
|
|
|
|
|
if Y_diff == 0:
|
|
|
|
|
assert X_diff >= minX, "node" + \
|
|
|
|
|
str(A) + " and node" + str(B) + \
|
|
|
|
|
" are too close in X. Minmum is " + str(minY)
|