mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into bisr
This commit is contained in:
commit
a5c211bd90
|
|
@ -47,8 +47,16 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
|
|
||||||
self.layer_stack = layer_stack
|
self.layer_stack = layer_stack
|
||||||
self.dimensions = dimensions
|
self.dimensions = dimensions
|
||||||
if directions:
|
|
||||||
|
# Non-preferred directions
|
||||||
|
if directions == "nonpref":
|
||||||
|
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
|
||||||
|
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
|
||||||
|
self.directions = (first_dir, second_dir)
|
||||||
|
# User directions
|
||||||
|
elif directions:
|
||||||
self.directions = directions
|
self.directions = directions
|
||||||
|
# Preferred directions
|
||||||
else:
|
else:
|
||||||
self.directions = (tech.preferred_directions[layer_stack[0]],
|
self.directions = (tech.preferred_directions[layer_stack[0]],
|
||||||
tech.preferred_directions[layer_stack[2]])
|
tech.preferred_directions[layer_stack[2]])
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from hierarchy_design import hierarchy_design
|
from hierarchy_design import hierarchy_design
|
||||||
|
from utils import round_to_grid
|
||||||
import contact
|
import contact
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import re
|
import re
|
||||||
|
|
@ -31,47 +32,97 @@ class design(hierarchy_design):
|
||||||
in many places in the compiler.
|
in many places in the compiler.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from tech import layer_indices
|
||||||
import tech
|
import tech
|
||||||
for key in dir(tech):
|
for layer in layer_indices:
|
||||||
# Single layer width rules
|
key = "{}_stack".format(layer)
|
||||||
match = re.match(r".*_stack$", key)
|
|
||||||
if match:
|
# Set the stack as a local helper
|
||||||
|
try:
|
||||||
layer_stack = getattr(tech, key)
|
layer_stack = getattr(tech, key)
|
||||||
|
|
||||||
# Set the stack as a local helper
|
|
||||||
setattr(self, key, layer_stack)
|
setattr(self, key, layer_stack)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
# Add the pitch
|
# Skip computing the pitch for active
|
||||||
setattr(self,
|
if layer == "active":
|
||||||
"{}_pitch".format(layer_stack[0]),
|
continue
|
||||||
self.compute_pitch(layer_stack))
|
|
||||||
|
# Add the pitch
|
||||||
|
setattr(self,
|
||||||
|
"{}_pitch".format(layer),
|
||||||
|
self.compute_pitch(layer, True))
|
||||||
|
|
||||||
|
# Add the non-preferrd pitch (which has vias in the "wrong" way)
|
||||||
|
setattr(self,
|
||||||
|
"{}_nonpref_pitch".format(layer),
|
||||||
|
self.compute_pitch(layer, False))
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
print("m1_pitch", self.m1_pitch)
|
from tech import preferred_directions
|
||||||
print("m2_pitch", self.m2_pitch)
|
print(preferred_directions)
|
||||||
print("m3_pitch", self.m3_pitch)
|
from tech import layer, layer_indices
|
||||||
|
for name in layer_indices:
|
||||||
|
if name == "active":
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
print("{0} width {1} space {2}".format(name,
|
||||||
|
getattr(self, "{}_width".format(name)),
|
||||||
|
getattr(self, "{}_space".format(name))))
|
||||||
|
|
||||||
|
print("pitch {0} nonpref {1}".format(getattr(self, "{}_pitch".format(name)),
|
||||||
|
getattr(self, "{}_nonpref_pitch".format(name))))
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
import sys
|
import sys
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def compute_pitch(self, layer_stack):
|
def compute_pitch(self, layer, preferred=True):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This is contact direction independent pitch,
|
This is the preferred direction pitch
|
||||||
i.e. we take the maximum contact dimension
|
i.e. we take the minimum or maximum contact dimension
|
||||||
"""
|
"""
|
||||||
|
# Find the layer stacks this is used in
|
||||||
|
from tech import layer_stacks
|
||||||
|
pitches = []
|
||||||
|
for stack in layer_stacks:
|
||||||
|
# Compute the pitch with both vias above and below (if they exist)
|
||||||
|
if stack[0] == layer:
|
||||||
|
pitches.append(self.compute_layer_pitch(stack, preferred))
|
||||||
|
if stack[2] == layer:
|
||||||
|
pitches.append(self.compute_layer_pitch(stack[::-1], True))
|
||||||
|
|
||||||
|
return max(pitches)
|
||||||
|
|
||||||
|
def compute_layer_pitch(self, layer_stack, preferred):
|
||||||
|
|
||||||
(layer1, via, layer2) = layer_stack
|
(layer1, via, layer2) = layer_stack
|
||||||
|
try:
|
||||||
|
if layer1 == "poly" or layer1 == "active":
|
||||||
|
contact1 = getattr(contact, layer1 + "_contact")
|
||||||
|
else:
|
||||||
|
contact1 = getattr(contact, layer1 + "_via")
|
||||||
|
except AttributeError:
|
||||||
|
contact1 = getattr(contact, layer2 + "_via")
|
||||||
|
|
||||||
if layer1 == "poly" or layer1 == "active":
|
if preferred:
|
||||||
contact1 = getattr(contact, layer1 + "_contact")
|
if self.get_preferred_direction(layer1) == "V":
|
||||||
|
contact_width = contact1.first_layer_width
|
||||||
|
else:
|
||||||
|
contact_width = contact1.first_layer_height
|
||||||
else:
|
else:
|
||||||
contact1 = getattr(contact, layer1 + "_via")
|
if self.get_preferred_direction(layer1) == "V":
|
||||||
max_contact = max(contact1.width, contact1.height)
|
contact_width = contact1.first_layer_height
|
||||||
|
else:
|
||||||
|
contact_width = contact1.first_layer_width
|
||||||
|
layer_space = getattr(self, layer1 + "_space")
|
||||||
|
|
||||||
layer1_space = getattr(self, layer1 + "_space")
|
#print(layer_stack)
|
||||||
layer2_space = getattr(self, layer2 + "_space")
|
#print(contact1)
|
||||||
pitch = max_contact + max(layer1_space, layer2_space)
|
pitch = contact_width + layer_space
|
||||||
|
|
||||||
return pitch
|
return round_to_grid(pitch)
|
||||||
|
|
||||||
def setup_drc_constants(self):
|
def setup_drc_constants(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import debug
|
||||||
from math import sqrt
|
from math import sqrt
|
||||||
from tech import drc, GDS
|
from tech import drc, GDS
|
||||||
from tech import layer as techlayer
|
from tech import layer as techlayer
|
||||||
|
from tech import layer_indices
|
||||||
from tech import layer_stacks
|
from tech import layer_stacks
|
||||||
import os
|
import os
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
@ -41,12 +42,15 @@ class layout():
|
||||||
self.visited = [] # List of modules we have already visited
|
self.visited = [] # List of modules we have already visited
|
||||||
self.is_library_cell = False # Flag for library cells
|
self.is_library_cell = False # Flag for library cells
|
||||||
self.gds_read()
|
self.gds_read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from tech import power_grid
|
from tech import power_grid
|
||||||
self.pwr_grid_layer = power_grid[0]
|
self.pwr_grid_layer = power_grid[0]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
self.pwr_grid_layer = "m3"
|
self.pwr_grid_layer = "m3"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# GDS layout
|
# GDS layout
|
||||||
############################################################
|
############################################################
|
||||||
|
|
@ -444,7 +448,40 @@ class layout():
|
||||||
path=coordinates,
|
path=coordinates,
|
||||||
layer_widths=layer_widths)
|
layer_widths=layer_widths)
|
||||||
|
|
||||||
def add_wire(self, layers, coordinates):
|
def add_zjog(self, layer, start, end, first_direction="H"):
|
||||||
|
"""
|
||||||
|
Add a simple jog at the halfway point.
|
||||||
|
If layer is a single value, it is a path.
|
||||||
|
If layer is a tuple, it is a wire with preferred directions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# vertical first
|
||||||
|
if first_direction == "V":
|
||||||
|
mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y)
|
||||||
|
mid2 = vector(end.x, mid1.y)
|
||||||
|
# horizontal first
|
||||||
|
elif first_direction == "H":
|
||||||
|
mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y)
|
||||||
|
mid2 = vector(mid1, end.y)
|
||||||
|
else:
|
||||||
|
debug.error("Invalid direction for jog -- must be H or V.")
|
||||||
|
|
||||||
|
if layer in layer_stacks:
|
||||||
|
self.add_wire(layer, [start, mid1, mid2, end])
|
||||||
|
elif layer in techlayer:
|
||||||
|
self.add_path(layer, [start, mid1, mid2, end])
|
||||||
|
else:
|
||||||
|
debug.error("Could not find layer {}".format(layer))
|
||||||
|
|
||||||
|
def add_horizontal_zjog_path(self, layer, start, end):
|
||||||
|
""" Add a simple jog at the halfway point """
|
||||||
|
|
||||||
|
# horizontal first
|
||||||
|
mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y)
|
||||||
|
mid2 = vector(mid1, end.y)
|
||||||
|
self.add_path(layer, [start, mid1, mid2, end])
|
||||||
|
|
||||||
|
def add_wire(self, layers, coordinates, widen_short_wires=True):
|
||||||
"""Connects a routing path on given layer,coordinates,width.
|
"""Connects a routing path on given layer,coordinates,width.
|
||||||
The layers are the (horizontal, via, vertical). """
|
The layers are the (horizontal, via, vertical). """
|
||||||
import wire
|
import wire
|
||||||
|
|
@ -452,7 +489,8 @@ class layout():
|
||||||
# into rectangles and contacts
|
# into rectangles and contacts
|
||||||
wire.wire(obj=self,
|
wire.wire(obj=self,
|
||||||
layer_stack=layers,
|
layer_stack=layers,
|
||||||
position_list=coordinates)
|
position_list=coordinates,
|
||||||
|
widen_short_wires=widen_short_wires)
|
||||||
|
|
||||||
def get_preferred_direction(self, layer):
|
def get_preferred_direction(self, layer):
|
||||||
""" Return the preferred routing directions """
|
""" Return the preferred routing directions """
|
||||||
|
|
@ -461,11 +499,6 @@ class layout():
|
||||||
|
|
||||||
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
|
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
|
||||||
""" Add a three layer via structure. """
|
""" Add a three layer via structure. """
|
||||||
|
|
||||||
if not directions:
|
|
||||||
directions = (self.get_preferred_direction(layers[0]),
|
|
||||||
self.get_preferred_direction(layers[2]))
|
|
||||||
|
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
via = factory.create(module_type="contact",
|
via = factory.create(module_type="contact",
|
||||||
layer_stack=layers,
|
layer_stack=layers,
|
||||||
|
|
@ -486,11 +519,6 @@ class layout():
|
||||||
Add a three layer via structure by the center coordinate
|
Add a three layer via structure by the center coordinate
|
||||||
accounting for mirroring and rotation.
|
accounting for mirroring and rotation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not directions:
|
|
||||||
directions = (self.get_preferred_direction(layers[0]),
|
|
||||||
self.get_preferred_direction(layers[2]))
|
|
||||||
|
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
via = factory.create(module_type="contact",
|
via = factory.create(module_type="contact",
|
||||||
layer_stack=layers,
|
layer_stack=layers,
|
||||||
|
|
@ -513,36 +541,47 @@ class layout():
|
||||||
return inst
|
return inst
|
||||||
|
|
||||||
def add_via_stack(self, offset, from_layer, to_layer,
|
def add_via_stack(self, offset, from_layer, to_layer,
|
||||||
direction=None,
|
directions=None,
|
||||||
size=[1, 1]):
|
size=[1, 1],
|
||||||
|
implant_type=None,
|
||||||
|
well_type=None):
|
||||||
"""
|
"""
|
||||||
Punch a stack of vias from a start layer to a target layer.
|
Punch a stack of vias from a start layer to a target layer.
|
||||||
"""
|
"""
|
||||||
return self.__add_via_stack_internal(offset=offset,
|
return self.__add_via_stack_internal(offset=offset,
|
||||||
direction=direction,
|
directions=directions,
|
||||||
from_layer=from_layer,
|
from_layer=from_layer,
|
||||||
to_layer=to_layer,
|
to_layer=to_layer,
|
||||||
via_func=self.add_via,
|
via_func=self.add_via,
|
||||||
last_via=None,
|
last_via=None,
|
||||||
size=size)
|
size=size,
|
||||||
|
implant_type=implant_type,
|
||||||
|
well_type=well_type)
|
||||||
|
|
||||||
def add_via_stack_center(self, offset, from_layer, to_layer,
|
def add_via_stack_center(self,
|
||||||
direction=None,
|
offset,
|
||||||
size=[1, 1]):
|
from_layer,
|
||||||
|
to_layer,
|
||||||
|
directions=None,
|
||||||
|
size=[1, 1],
|
||||||
|
implant_type=None,
|
||||||
|
well_type=None):
|
||||||
"""
|
"""
|
||||||
Punch a stack of vias from a start layer to a target layer by the center
|
Punch a stack of vias from a start layer to a target layer by the center
|
||||||
coordinate accounting for mirroring and rotation.
|
coordinate accounting for mirroring and rotation.
|
||||||
"""
|
"""
|
||||||
return self.__add_via_stack_internal(offset=offset,
|
return self.__add_via_stack_internal(offset=offset,
|
||||||
direction=direction,
|
directions=directions,
|
||||||
from_layer=from_layer,
|
from_layer=from_layer,
|
||||||
to_layer=to_layer,
|
to_layer=to_layer,
|
||||||
via_func=self.add_via_center,
|
via_func=self.add_via_center,
|
||||||
last_via=None,
|
last_via=None,
|
||||||
size=size)
|
size=size,
|
||||||
|
implant_type=implant_type,
|
||||||
|
well_type=well_type)
|
||||||
|
|
||||||
def __add_via_stack_internal(self, offset, direction, from_layer, to_layer,
|
def __add_via_stack_internal(self, offset, directions, from_layer, to_layer,
|
||||||
via_func, last_via, size):
|
via_func, last_via, size, implant_type=None, well_type=None):
|
||||||
"""
|
"""
|
||||||
Punch a stack of vias from a start layer to a target layer. Here we
|
Punch a stack of vias from a start layer to a target layer. Here we
|
||||||
figure out whether to punch it up or down the stack.
|
figure out whether to punch it up or down the stack.
|
||||||
|
|
@ -551,8 +590,8 @@ class layout():
|
||||||
if from_layer == to_layer:
|
if from_layer == to_layer:
|
||||||
return last_via
|
return last_via
|
||||||
|
|
||||||
from_id = int(from_layer[1])
|
from_id = layer_indices[from_layer]
|
||||||
to_id = int(to_layer[1])
|
to_id = layer_indices[to_layer]
|
||||||
|
|
||||||
if from_id < to_id: # grow the stack up
|
if from_id < to_id: # grow the stack up
|
||||||
search_id = 0
|
search_id = 0
|
||||||
|
|
@ -563,18 +602,24 @@ class layout():
|
||||||
|
|
||||||
curr_stack = next(filter(lambda stack: stack[search_id] == from_layer, layer_stacks), None)
|
curr_stack = next(filter(lambda stack: stack[search_id] == from_layer, layer_stacks), None)
|
||||||
if curr_stack is None:
|
if curr_stack is None:
|
||||||
raise ValueError("Cannot create via from '{0}' to '{1}'." \
|
raise ValueError("Cannot create via from '{0}' to '{1}'."
|
||||||
"Layer '{0}' not defined"
|
"Layer '{0}' not defined".format(from_layer, to_layer))
|
||||||
.format(from_layer, to_layer))
|
|
||||||
|
|
||||||
via = via_func(layers=curr_stack, size=size, offset=offset, directions=direction)
|
via = via_func(layers=curr_stack,
|
||||||
return self.__add_via_stack_internal(offset=offset,
|
size=size,
|
||||||
direction=direction,
|
offset=offset,
|
||||||
from_layer=curr_stack[next_id],
|
directions=directions,
|
||||||
to_layer=to_layer,
|
implant_type=implant_type,
|
||||||
via_func=via_func,
|
well_type=well_type)
|
||||||
last_via=via,
|
|
||||||
size=size)
|
via = self.__add_via_stack_internal(offset=offset,
|
||||||
|
directions=directions,
|
||||||
|
from_layer=curr_stack[next_id],
|
||||||
|
to_layer=to_layer,
|
||||||
|
via_func=via_func,
|
||||||
|
last_via=via,
|
||||||
|
size=size)
|
||||||
|
return via
|
||||||
|
|
||||||
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
|
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
|
||||||
"""Adds a ptx module to the design."""
|
"""Adds a ptx module to the design."""
|
||||||
|
|
@ -729,47 +774,47 @@ class layout():
|
||||||
|
|
||||||
return blockages
|
return blockages
|
||||||
|
|
||||||
def create_horizontal_pin_bus(self, layer, pitch, offset, names, length):
|
def create_horizontal_pin_bus(self, layer, offset, names, length, pitch=None):
|
||||||
""" Create a horizontal bus of pins. """
|
""" Create a horizontal bus of pins. """
|
||||||
return self.create_bus(layer,
|
return self.create_bus(layer,
|
||||||
pitch,
|
|
||||||
offset,
|
offset,
|
||||||
names,
|
names,
|
||||||
length,
|
length,
|
||||||
vertical=False,
|
vertical=False,
|
||||||
make_pins=True)
|
make_pins=True,
|
||||||
|
pitch=pitch)
|
||||||
|
|
||||||
def create_vertical_pin_bus(self, layer, pitch, offset, names, length):
|
def create_vertical_pin_bus(self, layer, offset, names, length, pitch=None):
|
||||||
""" Create a horizontal bus of pins. """
|
""" Create a horizontal bus of pins. """
|
||||||
return self.create_bus(layer,
|
return self.create_bus(layer,
|
||||||
pitch,
|
|
||||||
offset,
|
offset,
|
||||||
names,
|
names,
|
||||||
length,
|
length,
|
||||||
vertical=True,
|
vertical=True,
|
||||||
make_pins=True)
|
make_pins=True,
|
||||||
|
pitch=pitch)
|
||||||
|
|
||||||
def create_vertical_bus(self, layer, pitch, offset, names, length):
|
def create_vertical_bus(self, layer, offset, names, length, pitch=None):
|
||||||
""" Create a horizontal bus. """
|
""" Create a horizontal bus. """
|
||||||
return self.create_bus(layer,
|
return self.create_bus(layer,
|
||||||
pitch,
|
|
||||||
offset,
|
offset,
|
||||||
names,
|
names,
|
||||||
length,
|
length,
|
||||||
vertical=True,
|
vertical=True,
|
||||||
make_pins=False)
|
make_pins=False,
|
||||||
|
pitch=pitch)
|
||||||
|
|
||||||
def create_horizontal_bus(self, layer, pitch, offset, names, length):
|
def create_horizontal_bus(self, layer, offset, names, length, pitch=None):
|
||||||
""" Create a horizontal bus. """
|
""" Create a horizontal bus. """
|
||||||
return self.create_bus(layer,
|
return self.create_bus(layer,
|
||||||
pitch,
|
|
||||||
offset,
|
offset,
|
||||||
names,
|
names,
|
||||||
length,
|
length,
|
||||||
vertical=False,
|
vertical=False,
|
||||||
make_pins=False)
|
make_pins=False,
|
||||||
|
pitch=pitch)
|
||||||
|
|
||||||
def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins):
|
def create_bus(self, layer, offset, names, length, vertical, make_pins, pitch=None):
|
||||||
"""
|
"""
|
||||||
Create a horizontal or vertical bus. It can be either just rectangles, or actual
|
Create a horizontal or vertical bus. It can be either just rectangles, or actual
|
||||||
layout pins. It returns an map of line center line positions indexed by name.
|
layout pins. It returns an map of line center line positions indexed by name.
|
||||||
|
|
@ -779,11 +824,14 @@ class layout():
|
||||||
|
|
||||||
# half minwidth so we can return the center line offsets
|
# half minwidth so we can return the center line offsets
|
||||||
half_minwidth = 0.5 * drc["minwidth_{}".format(layer)]
|
half_minwidth = 0.5 * drc["minwidth_{}".format(layer)]
|
||||||
|
if not pitch:
|
||||||
|
pitch = getattr(self, "{}_pitch".format(layer))
|
||||||
|
|
||||||
line_positions = {}
|
line_positions = {}
|
||||||
if vertical:
|
if vertical:
|
||||||
for i in range(len(names)):
|
for i in range(len(names)):
|
||||||
line_offset = offset + vector(i * pitch, 0)
|
line_offset = offset + vector(i * pitch,
|
||||||
|
0)
|
||||||
if make_pins:
|
if make_pins:
|
||||||
self.add_layout_pin(text=names[i],
|
self.add_layout_pin(text=names[i],
|
||||||
layer=layer,
|
layer=layer,
|
||||||
|
|
@ -848,8 +896,10 @@ class layout():
|
||||||
# left/right then up/down
|
# left/right then up/down
|
||||||
mid_pos = vector(bus_pos.x, pin_pos.y)
|
mid_pos = vector(bus_pos.x, pin_pos.y)
|
||||||
|
|
||||||
|
# Don't widen short wires because pin_pos and mid_pos could be really close
|
||||||
self.add_wire(layer_stack,
|
self.add_wire(layer_stack,
|
||||||
[bus_pos, mid_pos, pin_pos])
|
[bus_pos, mid_pos, pin_pos],
|
||||||
|
widen_short_wires=False)
|
||||||
|
|
||||||
# Connect to the pin on the instances with a via if it is
|
# Connect to the pin on the instances with a via if it is
|
||||||
# not on the right layer
|
# not on the right layer
|
||||||
|
|
@ -866,32 +916,75 @@ class layout():
|
||||||
offset=bus_pos,
|
offset=bus_pos,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
def connect_vbus(self, src_pin, dest_pin, hlayer="m3", vlayer="m2"):
|
||||||
|
"""
|
||||||
|
Helper routine to connect an instance to a vertical bus.
|
||||||
|
Routes horizontal then vertical L shape.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if src_pin.cx()<dest_pin.cx():
|
||||||
|
in_pos = src_pin.rc()
|
||||||
|
else:
|
||||||
|
in_pos = src_pin.lc()
|
||||||
|
if src_pin.cy() < dest_pin.cy():
|
||||||
|
out_pos = dest_pin.bc()
|
||||||
|
else:
|
||||||
|
out_pos = dest_pin.uc()
|
||||||
|
|
||||||
|
# move horizontal first on layer stack
|
||||||
|
mid_pos = vector(out_pos.x, in_pos.y)
|
||||||
|
self.add_via_stack_center(from_layer=src_pin.layer,
|
||||||
|
to_layer=hlayer,
|
||||||
|
offset=in_pos)
|
||||||
|
self.add_path(hlayer, [in_pos, mid_pos])
|
||||||
|
self.add_via_stack_center(from_layer=hlayer,
|
||||||
|
to_layer=vlayer,
|
||||||
|
offset=mid_pos)
|
||||||
|
self.add_path(vlayer, [mid_pos, out_pos])
|
||||||
|
self.add_via_stack_center(from_layer=vlayer,
|
||||||
|
to_layer=dest_pin.layer,
|
||||||
|
offset=out_pos)
|
||||||
|
|
||||||
|
def connect_hbus(self, src_pin, dest_pin, hlayer="m3", vlayer="m2"):
|
||||||
|
"""
|
||||||
|
Helper routine to connect an instance to a horizontal bus.
|
||||||
|
Routes horizontal then vertical L shape.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if src_pin.cx()<dest_pin.cx():
|
||||||
|
in_pos = src_pin.rc()
|
||||||
|
else:
|
||||||
|
in_pos = src_pin.lc()
|
||||||
|
if src_pin.cy() < dest_pin.cy():
|
||||||
|
out_pos = dest_pin.lc()
|
||||||
|
else:
|
||||||
|
out_pos = dest_pin.rc()
|
||||||
|
|
||||||
|
# move horizontal first
|
||||||
|
mid_pos = vector(out_pos.x, in_pos.y)
|
||||||
|
self.add_via_stack_center(from_layer=src_pin.layer,
|
||||||
|
to_layer=hlayer,
|
||||||
|
offset=in_pos)
|
||||||
|
self.add_path(hlayer, [in_pos, mid_pos])
|
||||||
|
self.add_via_stack_center(from_layer=hlayer,
|
||||||
|
to_layer=vlayer,
|
||||||
|
offset=mid_pos)
|
||||||
|
self.add_path(vlayer, [mid_pos, out_pos])
|
||||||
|
self.add_via_stack_center(from_layer=vlayer,
|
||||||
|
to_layer=dest_pin.layer,
|
||||||
|
offset=out_pos)
|
||||||
|
|
||||||
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 """
|
||||||
if layer == "m1":
|
try:
|
||||||
return (self.m1_pitch,
|
# FIXME: Using non-pref pitch here due to overlap bug in VCG constraints.
|
||||||
self.m1_pitch - self.m1_space,
|
# It should just result in inefficient channel width but will work.
|
||||||
self.m1_space)
|
pitch = getattr(self, "{}_pitch".format(layer))
|
||||||
elif layer == "m2":
|
nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer))
|
||||||
return (self.m2_pitch,
|
space = getattr(self, "{}_space".format(layer))
|
||||||
self.m2_pitch - self.m2_space,
|
except AttributeError:
|
||||||
self.m2_space)
|
debug.error("Cannot find layer pitch.", -1)
|
||||||
elif layer == "m3":
|
return (nonpref_pitch, pitch, pitch - space, space)
|
||||||
return (self.m3_pitch,
|
|
||||||
self.m3_pitch - self.m3_space,
|
|
||||||
self.m3_space)
|
|
||||||
elif layer == "m4":
|
|
||||||
from tech import layer as tech_layer
|
|
||||||
if "m4" in tech_layer:
|
|
||||||
return (self.m3_pitch,
|
|
||||||
self.m3_pitch - self.m4_space,
|
|
||||||
self.m4_space)
|
|
||||||
else:
|
|
||||||
return (self.m3_pitch,
|
|
||||||
self.m3_pitch - self.m3_space,
|
|
||||||
self.m3_space)
|
|
||||||
else:
|
|
||||||
debug.error("Cannot find layer pitch.")
|
|
||||||
|
|
||||||
def add_horizontal_trunk_route(self,
|
def add_horizontal_trunk_route(self,
|
||||||
pins,
|
pins,
|
||||||
|
|
@ -906,7 +999,7 @@ class layout():
|
||||||
min_x = min([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
|
# if we are less than a pitch, just create a non-preferred layer jog
|
||||||
if max_x-min_x <= pitch:
|
if max_x - min_x <= pitch:
|
||||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
|
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
|
||||||
|
|
||||||
# Add the horizontal trunk on the vertical layer!
|
# Add the horizontal trunk on the vertical layer!
|
||||||
|
|
@ -975,14 +1068,15 @@ class layout():
|
||||||
def create_channel_route(self, netlist,
|
def create_channel_route(self, netlist,
|
||||||
offset,
|
offset,
|
||||||
layer_stack,
|
layer_stack,
|
||||||
layer_dirs=None,
|
directions=None,
|
||||||
vertical=False):
|
vertical=False):
|
||||||
"""
|
"""
|
||||||
The net list is a list of the nets. Each net is a list of pins
|
The net list is a list of the nets with each net being a list of pins
|
||||||
to be connected. Offset is the lower-left of where the
|
to be connected. The offset is the lower-left of where the
|
||||||
routing channel will start. This does NOT try to minimize the
|
routing channel will start. This does NOT try to minimize the
|
||||||
number of tracks -- instead, it picks an order to avoid the
|
number of tracks -- instead, it picks an order to avoid the
|
||||||
vertical conflicts between pins.
|
vertical conflicts between pins. The track size must be the number of
|
||||||
|
nets times the *nonpreferred* routing of the non-track layer pitch.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def remove_net_from_graph(pin, g):
|
def remove_net_from_graph(pin, g):
|
||||||
|
|
@ -999,12 +1093,17 @@ class layout():
|
||||||
g[other_pin]=conflicts
|
g[other_pin]=conflicts
|
||||||
return g
|
return g
|
||||||
|
|
||||||
def vcg_nets_overlap(net1, net2, vertical, pitch):
|
def vcg_nets_overlap(net1, net2, vertical):
|
||||||
"""
|
"""
|
||||||
Check all the pin pairs on two nets and return a pin
|
Check all the pin pairs on two nets and return a pin
|
||||||
overlap if any pin overlaps.
|
overlap if any pin overlaps.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if vertical:
|
||||||
|
pitch = self.horizontal_nonpref_pitch
|
||||||
|
else:
|
||||||
|
pitch = self.vertical_nonpref_pitch
|
||||||
|
|
||||||
for pin1 in net1:
|
for pin1 in net1:
|
||||||
for pin2 in net2:
|
for pin2 in net2:
|
||||||
if vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
if vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
||||||
|
|
@ -1017,8 +1116,7 @@ class layout():
|
||||||
|
|
||||||
# FIXME: If the pins are not in a row, this may break.
|
# FIXME: If the pins are not in a row, this may break.
|
||||||
# However, a top pin shouldn't overlap another top pin,
|
# However, a top pin shouldn't overlap another top pin,
|
||||||
# for example, so the
|
# for example, so the extra comparison *shouldn't* matter.
|
||||||
# extra comparison *shouldn't* matter.
|
|
||||||
|
|
||||||
# Pin 1 must be in the "BOTTOM" set
|
# Pin 1 must be in the "BOTTOM" set
|
||||||
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
|
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
|
||||||
|
|
@ -1028,7 +1126,7 @@ class layout():
|
||||||
overlaps = (not vertical and x_overlap) or (vertical and y_overlap)
|
overlaps = (not vertical and x_overlap) or (vertical and y_overlap)
|
||||||
return overlaps
|
return overlaps
|
||||||
|
|
||||||
if not layer_dirs:
|
if not directions:
|
||||||
# Use the preferred layer directions
|
# Use the preferred layer directions
|
||||||
if self.get_preferred_direction(layer_stack[0]) == "V":
|
if self.get_preferred_direction(layer_stack[0]) == "V":
|
||||||
self.vertical_layer = layer_stack[0]
|
self.vertical_layer = layer_stack[0]
|
||||||
|
|
@ -1039,8 +1137,8 @@ class layout():
|
||||||
else:
|
else:
|
||||||
# Use the layer directions specified to the router rather than
|
# Use the layer directions specified to the router rather than
|
||||||
# the preferred directions
|
# the preferred directions
|
||||||
debug.check(layer_dirs[0] != layer_dirs[1], "Must have unique layer directions.")
|
debug.check(directions[0] != directions[1], "Must have unique layer directions.")
|
||||||
if layer_dirs[0] == "V":
|
if directions[0] == "V":
|
||||||
self.vertical_layer = layer_stack[0]
|
self.vertical_layer = layer_stack[0]
|
||||||
self.horizontal_layer = layer_stack[2]
|
self.horizontal_layer = layer_stack[2]
|
||||||
else:
|
else:
|
||||||
|
|
@ -1048,9 +1146,10 @@ class layout():
|
||||||
self.vertical_layer = layer_stack[2]
|
self.vertical_layer = layer_stack[2]
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.vertical_layer)
|
layer_stuff = self.get_layer_pitch(self.vertical_layer)
|
||||||
(self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
|
(self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
||||||
(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
|
||||||
|
|
||||||
# FIXME: Must extend this to a horizontal conflict graph
|
# FIXME: Must extend this to a horizontal conflict graph
|
||||||
# too if we want to minimize the
|
# too if we want to minimize the
|
||||||
|
|
@ -1070,6 +1169,10 @@ class layout():
|
||||||
index += 1
|
index += 1
|
||||||
nets[net_name] = pin_list
|
nets[net_name] = pin_list
|
||||||
|
|
||||||
|
# print("Nets:")
|
||||||
|
# for net_name in nets:
|
||||||
|
# print(net_name, [x.name for x in nets[net_name]])
|
||||||
|
|
||||||
# Find the vertical pin conflicts
|
# 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:
|
for net_name1 in nets:
|
||||||
|
|
@ -1081,21 +1184,15 @@ class layout():
|
||||||
# Skip yourself
|
# Skip yourself
|
||||||
if net_name1 == net_name2:
|
if net_name1 == net_name2:
|
||||||
continue
|
continue
|
||||||
if vertical and vcg_nets_overlap(nets[net_name1],
|
if vcg_nets_overlap(nets[net_name1],
|
||||||
nets[net_name2],
|
nets[net_name2],
|
||||||
vertical,
|
vertical):
|
||||||
self.vertical_pitch):
|
|
||||||
vcg[net_name2].append(net_name1)
|
|
||||||
elif not vertical and vcg_nets_overlap(nets[net_name1],
|
|
||||||
nets[net_name2],
|
|
||||||
vertical,
|
|
||||||
self.horizontal_pitch):
|
|
||||||
vcg[net_name2].append(net_name1)
|
vcg[net_name2].append(net_name1)
|
||||||
|
|
||||||
# list of routes to do
|
# list of routes to do
|
||||||
while vcg:
|
while vcg:
|
||||||
# from pprint import pformat
|
# from pprint import pformat
|
||||||
# print("VCG:\n",pformat(vcg))
|
# print("VCG:\n", pformat(vcg))
|
||||||
# get a route from conflict graph with empty fanout set
|
# get a route from conflict graph with empty fanout set
|
||||||
net_name = None
|
net_name = None
|
||||||
for net_name, conflicts in vcg.items():
|
for net_name, conflicts in vcg.items():
|
||||||
|
|
@ -1119,26 +1216,28 @@ class layout():
|
||||||
self.add_vertical_trunk_route(pin_list,
|
self.add_vertical_trunk_route(pin_list,
|
||||||
offset,
|
offset,
|
||||||
layer_stack,
|
layer_stack,
|
||||||
self.vertical_pitch)
|
self.vertical_nonpref_pitch)
|
||||||
offset += vector(self.vertical_pitch, 0)
|
# This accounts for the via-to-via spacings
|
||||||
|
offset += vector(self.horizontal_nonpref_pitch, 0)
|
||||||
else:
|
else:
|
||||||
self.add_horizontal_trunk_route(pin_list,
|
self.add_horizontal_trunk_route(pin_list,
|
||||||
offset,
|
offset,
|
||||||
layer_stack,
|
layer_stack,
|
||||||
self.horizontal_pitch)
|
self.horizontal_nonpref_pitch)
|
||||||
offset += vector(0, self.horizontal_pitch)
|
# This accounts for the via-to-via spacings
|
||||||
|
offset += vector(0, self.vertical_nonpref_pitch)
|
||||||
|
|
||||||
def create_vertical_channel_route(self, netlist, offset, layer_stack, layer_dirs=None):
|
def create_vertical_channel_route(self, netlist, offset, layer_stack, directions=None):
|
||||||
"""
|
"""
|
||||||
Wrapper to create a vertical channel route
|
Wrapper to create a vertical channel route
|
||||||
"""
|
"""
|
||||||
self.create_channel_route(netlist, offset, layer_stack, layer_dirs, vertical=True)
|
self.create_channel_route(netlist, offset, layer_stack, directions, vertical=True)
|
||||||
|
|
||||||
def create_horizontal_channel_route(self, netlist, offset, layer_stack, layer_dirs=None):
|
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
|
||||||
"""
|
"""
|
||||||
Wrapper to create a horizontal channel route
|
Wrapper to create a horizontal channel route
|
||||||
"""
|
"""
|
||||||
self.create_channel_route(netlist, offset, layer_stack, layer_dirs, vertical=False)
|
self.create_channel_route(netlist, offset, layer_stack, directions, vertical=False)
|
||||||
|
|
||||||
def add_boundary(self, ll=vector(0, 0), ur=None):
|
def add_boundary(self, ll=vector(0, 0), ur=None):
|
||||||
""" Add boundary for debugging dimensions """
|
""" Add boundary for debugging dimensions """
|
||||||
|
|
@ -1200,27 +1299,18 @@ class layout():
|
||||||
"supply router."
|
"supply router."
|
||||||
.format(name, inst.name, self.pwr_grid_layer))
|
.format(name, inst.name, self.pwr_grid_layer))
|
||||||
|
|
||||||
def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"):
|
def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"):
|
||||||
"""
|
"""
|
||||||
Add a single power pin from the lowest power_grid layer down to M1 (or li) at
|
Add a single power pin from the lowest power_grid layer down to M1 (or li) at
|
||||||
the given center location. The starting layer is specified to determine
|
the given center location. The starting layer is specified to determine
|
||||||
which vias are needed.
|
which vias are needed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Force vdd/gnd via stack to be vertically or horizontally oriented
|
|
||||||
# Default: None, uses prefered metal directions
|
|
||||||
if vertical:
|
|
||||||
direction = ("V", "V")
|
|
||||||
elif not vertical and vertical is not None:
|
|
||||||
direction = ("H", "H")
|
|
||||||
else:
|
|
||||||
direction = None
|
|
||||||
|
|
||||||
via = self.add_via_stack_center(from_layer=start_layer,
|
via = self.add_via_stack_center(from_layer=start_layer,
|
||||||
to_layer=self.pwr_grid_layer,
|
to_layer=self.pwr_grid_layer,
|
||||||
size=size,
|
size=size,
|
||||||
offset=loc,
|
offset=loc,
|
||||||
direction=direction)
|
directions=directions)
|
||||||
if start_layer == self.pwr_grid_layer:
|
if start_layer == self.pwr_grid_layer:
|
||||||
self.add_layout_pin_rect_center(text=name,
|
self.add_layout_pin_rect_center(text=name,
|
||||||
layer=self.pwr_grid_layer,
|
layer=self.pwr_grid_layer,
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,14 @@ class wire(wire_path):
|
||||||
not, it will always go down first.
|
not, it will always go down first.
|
||||||
The points are the center of the wire.
|
The points are the center of the wire.
|
||||||
The layer stack is the vertical, contact/via, and horizontal layers, respectively.
|
The layer stack is the vertical, contact/via, and horizontal layers, respectively.
|
||||||
|
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)
|
||||||
"""
|
"""
|
||||||
def __init__(self, obj, layer_stack, position_list):
|
def __init__(self, obj, layer_stack, position_list, widen_short_wires=True):
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self.layer_stack = layer_stack
|
self.layer_stack = layer_stack
|
||||||
self.position_list = position_list
|
self.position_list = position_list
|
||||||
|
self.widen_short_wires = widen_short_wires
|
||||||
self.pins = [] # used for matching parm lengths
|
self.pins = [] # used for matching parm lengths
|
||||||
self.switch_pos_list = []
|
self.switch_pos_list = []
|
||||||
|
|
||||||
|
|
@ -114,7 +117,7 @@ class wire(wire_path):
|
||||||
line_length = pl[index + 1][0] - pl[index][0]
|
line_length = pl[index + 1][0] - pl[index][0]
|
||||||
# Make the wire wider to avoid via-to-via spacing problems
|
# Make the wire wider to avoid via-to-via spacing problems
|
||||||
# But don't make it wider if it is shorter than one via
|
# But don't make it wider if it is shorter than one via
|
||||||
if abs(line_length) < self.pitch and abs(line_length) > self.horiz_layer_contact_width:
|
if self.widen_short_wires and abs(line_length) < self.pitch and abs(line_length) > self.horiz_layer_contact_width:
|
||||||
width = self.horiz_layer_contact_width
|
width = self.horiz_layer_contact_width
|
||||||
else:
|
else:
|
||||||
width = self.horiz_layer_width
|
width = self.horiz_layer_width
|
||||||
|
|
@ -134,7 +137,7 @@ class wire(wire_path):
|
||||||
line_length = pl[index + 1][1] - pl[index][1]
|
line_length = pl[index + 1][1] - pl[index][1]
|
||||||
# Make the wire wider to avoid via-to-via spacing problems
|
# Make the wire wider to avoid via-to-via spacing problems
|
||||||
# But don't make it wider if it is shorter than one via
|
# But don't make it wider if it is shorter than one via
|
||||||
if abs(line_length) < self.pitch and abs(line_length) > self.vert_layer_contact_width:
|
if self.widen_short_wires and abs(line_length) < self.pitch and abs(line_length) > self.vert_layer_contact_width:
|
||||||
width = self.vert_layer_contact_width
|
width = self.vert_layer_contact_width
|
||||||
else:
|
else:
|
||||||
width = self.vert_layer_width
|
width = self.vert_layer_width
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ def create_rectilinear_route(my_list):
|
||||||
my_list.append(vector(pl[-1]))
|
my_list.append(vector(pl[-1]))
|
||||||
return my_list
|
return my_list
|
||||||
|
|
||||||
|
|
||||||
class wire_path():
|
class wire_path():
|
||||||
"""
|
"""
|
||||||
Object metal wire_path; given the layer type
|
Object metal wire_path; given the layer type
|
||||||
|
|
@ -37,7 +38,7 @@ class wire_path():
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self.layer_name = layer
|
self.layer_name = layer
|
||||||
self.layer_id = techlayer[layer]
|
self.layer_id = techlayer[layer]
|
||||||
if width==None:
|
if width == None:
|
||||||
self.layer_width = drc["minwidth_{0}".format(layer)]
|
self.layer_width = drc["minwidth_{0}".format(layer)]
|
||||||
else:
|
else:
|
||||||
self.layer_width = width
|
self.layer_width = width
|
||||||
|
|
@ -46,7 +47,6 @@ class wire_path():
|
||||||
self.switch_pos_list = []
|
self.switch_pos_list = []
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.create_rectilinear()
|
self.create_rectilinear()
|
||||||
self.connect_corner()
|
self.connect_corner()
|
||||||
|
|
@ -60,9 +60,9 @@ class wire_path():
|
||||||
|
|
||||||
def connect_corner(self):
|
def connect_corner(self):
|
||||||
""" Add a corner square at every corner of the wire_path."""
|
""" Add a corner square at every corner of the wire_path."""
|
||||||
from itertools import tee,islice
|
from itertools import tee, islice
|
||||||
nwise = lambda g,n=2: zip(*(islice(g,i,None) for i,g in enumerate(tee(g,n))))
|
nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
|
||||||
threewise=nwise(self.position_list,3)
|
threewise=nwise(self.position_list, 3)
|
||||||
|
|
||||||
for (a, offset, c) in list(threewise):
|
for (a, offset, c) in list(threewise):
|
||||||
# add a exceptions to prevent a corner when we retrace back in the same direction
|
# add a exceptions to prevent a corner when we retrace back in the same direction
|
||||||
|
|
@ -74,7 +74,6 @@ class wire_path():
|
||||||
offset[1] - 0.5 * self.layer_width]
|
offset[1] - 0.5 * self.layer_width]
|
||||||
self.draw_corner_wire(corner_offset)
|
self.draw_corner_wire(corner_offset)
|
||||||
|
|
||||||
|
|
||||||
def draw_corner_wire(self, offset):
|
def draw_corner_wire(self, offset):
|
||||||
""" This function adds the corner squares since the center
|
""" This function adds the corner squares since the center
|
||||||
line convention only draws to the center of the corner."""
|
line convention only draws to the center of the corner."""
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
+ 2 * self.implant_enclose_active \
|
+ 2 * self.implant_enclose_active \
|
||||||
+ 0.5*(self.inverter_pmos.active_contact.height - self.m1_width)
|
+ 0.5*(self.inverter_pmos.active_contact.height - self.m1_width)
|
||||||
metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space
|
metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space
|
||||||
self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width
|
self.vdd_offset = max(implant_constraint, metal1_constraint) + self.m1_width
|
||||||
|
|
||||||
# read port dimensions
|
# read port dimensions
|
||||||
width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx()
|
width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx()
|
||||||
|
|
@ -275,7 +275,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
Calculate positions that describe the edges
|
Calculate positions that describe the edges
|
||||||
and dimensions of the cell
|
and dimensions of the cell
|
||||||
"""
|
"""
|
||||||
self.botmost_ypos = self.m1_offset - self.total_ports * self.m1_pitch
|
self.botmost_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch
|
||||||
self.topmost_ypos = self.inverter_nmos_ypos \
|
self.topmost_ypos = self.inverter_nmos_ypos \
|
||||||
+ self.inverter_nmos.active_height \
|
+ self.inverter_nmos.active_height \
|
||||||
+ self.inverter_gap \
|
+ self.inverter_gap \
|
||||||
|
|
@ -378,14 +378,15 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
+ 0.5 * contact.poly_contact.height,
|
+ 0.5 * contact.poly_contact.height,
|
||||||
self.cross_couple_upper_ypos)
|
self.cross_couple_upper_ypos)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=contact_offset_left)
|
offset=contact_offset_left,
|
||||||
|
directions=("H", "H"))
|
||||||
|
|
||||||
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
||||||
- 0.5*contact.poly_contact.height,
|
- 0.5*contact.poly_contact.height,
|
||||||
self.cross_couple_lower_ypos)
|
self.cross_couple_lower_ypos)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=contact_offset_right)
|
offset=contact_offset_right,
|
||||||
|
directions=("H", "H"))
|
||||||
|
|
||||||
# connect contacts to gate poly (cross couple connections)
|
# connect contacts to gate poly (cross couple connections)
|
||||||
gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x,
|
gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x,
|
||||||
|
|
@ -399,12 +400,12 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
def route_rails(self):
|
def route_rails(self):
|
||||||
""" Adds gnd and vdd rails and connects them to the inverters """
|
""" Adds gnd and vdd rails and connects them to the inverters """
|
||||||
# Add rails for vdd and gnd
|
# Add rails for vdd and gnd
|
||||||
gnd_ypos = self.m1_offset - self.total_ports * self.m1_pitch
|
gnd_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch
|
||||||
self.gnd_position = vector(0, gnd_ypos)
|
self.gnd_position = vector(0, gnd_ypos)
|
||||||
self.add_rect_center(layer="m1",
|
self.add_rect_center(layer="m1",
|
||||||
offset=self.gnd_position,
|
offset=self.gnd_position,
|
||||||
width=self.width)
|
width=self.width)
|
||||||
self.add_power_pin("gnd", vector(0, gnd_ypos))
|
self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
|
||||||
|
|
||||||
|
|
||||||
vdd_ypos = self.inverter_nmos_ypos \
|
vdd_ypos = self.inverter_nmos_ypos \
|
||||||
|
|
@ -416,7 +417,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.add_rect_center(layer="m1",
|
self.add_rect_center(layer="m1",
|
||||||
offset=self.vdd_position,
|
offset=self.vdd_position,
|
||||||
width=self.width)
|
width=self.width)
|
||||||
self.add_power_pin("vdd", vector(0, vdd_ypos))
|
self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
|
||||||
|
|
||||||
def create_readwrite_ports(self):
|
def create_readwrite_ports(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -483,7 +484,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.port_ypos])
|
self.port_ypos])
|
||||||
|
|
||||||
# add pin for RWWL
|
# add pin for RWWL
|
||||||
rwwl_ypos = self.m1_offset - k * self.m1_pitch
|
rwwl_ypos = self.m1_offset - k * self.m1_nonpref_pitch
|
||||||
self.rwwl_positions[k] = vector(0, rwwl_ypos)
|
self.rwwl_positions[k] = vector(0, rwwl_ypos)
|
||||||
self.add_layout_pin_rect_center(text=self.rw_wl_names[k],
|
self.add_layout_pin_rect_center(text=self.rw_wl_names[k],
|
||||||
layer="m1",
|
layer="m1",
|
||||||
|
|
@ -579,8 +580,8 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
|
|
||||||
# add pin for WWL
|
# add pin for WWL
|
||||||
wwl_ypos = rwwl_ypos = self.m1_offset \
|
wwl_ypos = rwwl_ypos = self.m1_offset \
|
||||||
- self.num_rw_ports * self.m1_pitch \
|
- self.num_rw_ports * self.m1_nonpref_pitch \
|
||||||
- k * self.m1_pitch
|
- k * self.m1_nonpref_pitch
|
||||||
self.wwl_positions[k] = vector(0, wwl_ypos)
|
self.wwl_positions[k] = vector(0, wwl_ypos)
|
||||||
self.add_layout_pin_rect_center(text=self.w_wl_names[k],
|
self.add_layout_pin_rect_center(text=self.w_wl_names[k],
|
||||||
layer="m1",
|
layer="m1",
|
||||||
|
|
@ -705,9 +706,9 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
|
|
||||||
# add pin for RWL
|
# add pin for RWL
|
||||||
rwl_ypos = rwwl_ypos = self.m1_offset \
|
rwl_ypos = rwwl_ypos = self.m1_offset \
|
||||||
- self.num_rw_ports * self.m1_pitch \
|
- self.num_rw_ports * self.m1_nonpref_pitch \
|
||||||
- self.num_w_ports * self.m1_pitch \
|
- self.num_w_ports * self.m1_nonpref_pitch \
|
||||||
- k * self.m1_pitch
|
- k * self.m1_nonpref_pitch
|
||||||
self.rwl_positions[k] = vector(0, rwl_ypos)
|
self.rwl_positions[k] = vector(0, rwl_ypos)
|
||||||
self.add_layout_pin_rect_center(text=self.r_wl_names[k],
|
self.add_layout_pin_rect_center(text=self.r_wl_names[k],
|
||||||
layer="m1",
|
layer="m1",
|
||||||
|
|
@ -771,6 +772,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=port_contact_offset)
|
offset=port_contact_offset)
|
||||||
|
|
||||||
|
|
||||||
self.add_path("poly", [gate_offset, port_contact_offset])
|
self.add_path("poly", [gate_offset, port_contact_offset])
|
||||||
self.add_path("m1",
|
self.add_path("m1",
|
||||||
[port_contact_offset, wl_contact_offset])
|
[port_contact_offset, wl_contact_offset])
|
||||||
|
|
@ -821,7 +823,8 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
# Leave bitline disconnected if a dummy cell
|
# Leave bitline disconnected if a dummy cell
|
||||||
if not self.dummy_bitcell:
|
if not self.dummy_bitcell:
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=port_contact_offest)
|
offset=port_contact_offest,
|
||||||
|
directions="nonpref")
|
||||||
|
|
||||||
self.add_path("m2",
|
self.add_path("m2",
|
||||||
[port_contact_offest, bl_offset], width=contact.m1_via.height)
|
[port_contact_offest, bl_offset], width=contact.m1_via.height)
|
||||||
|
|
@ -833,7 +836,8 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
# Leave bitline disconnected if a dummy cell
|
# Leave bitline disconnected if a dummy cell
|
||||||
if not self.dummy_bitcell:
|
if not self.dummy_bitcell:
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=port_contact_offest)
|
offset=port_contact_offest,
|
||||||
|
directions="nonpref")
|
||||||
|
|
||||||
self.add_path("m2",
|
self.add_path("m2",
|
||||||
[port_contact_offest, br_offset], width=contact.m1_via.height)
|
[port_contact_offest, br_offset], width=contact.m1_via.height)
|
||||||
|
|
@ -850,7 +854,9 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
|
|
||||||
for position in nmos_contact_positions:
|
for position in nmos_contact_positions:
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=position)
|
offset=position,
|
||||||
|
directions=("V", "V"))
|
||||||
|
|
||||||
|
|
||||||
if position.x > 0:
|
if position.x > 0:
|
||||||
contact_correct = 0.5 * contact.m1_via.height
|
contact_correct = 0.5 * contact.m1_via.height
|
||||||
|
|
@ -859,7 +865,8 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
supply_offset = vector(position.x + contact_correct,
|
supply_offset = vector(position.x + contact_correct,
|
||||||
self.gnd_position.y)
|
self.gnd_position.y)
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=supply_offset)
|
offset=supply_offset,
|
||||||
|
directions=("H", "H"))
|
||||||
|
|
||||||
self.add_path("m2", [position, supply_offset])
|
self.add_path("m2", [position, supply_offset])
|
||||||
|
|
||||||
|
|
@ -924,13 +931,16 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
- self.poly_to_contact - 0.5*contact.poly_contact.width,
|
- self.poly_to_contact - 0.5*contact.poly_contact.width,
|
||||||
self.cross_couple_upper_ypos)
|
self.cross_couple_upper_ypos)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=left_storage_contact)
|
offset=left_storage_contact,
|
||||||
|
directions=("H", "H"))
|
||||||
|
|
||||||
|
|
||||||
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \
|
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \
|
||||||
+ self.poly_to_contact + 0.5*contact.poly_contact.width,
|
+ self.poly_to_contact + 0.5*contact.poly_contact.width,
|
||||||
self.cross_couple_upper_ypos)
|
self.cross_couple_upper_ypos)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=right_storage_contact)
|
offset=right_storage_contact,
|
||||||
|
directions=("H", "H"))
|
||||||
|
|
||||||
inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos)
|
inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos)
|
||||||
self.add_path("poly", [left_storage_contact, inverter_gate_offset_left])
|
self.add_path("poly", [left_storage_contact, inverter_gate_offset_left])
|
||||||
|
|
@ -1007,8 +1017,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
well_height = self.vdd_position.y - inverter_well_ypos \
|
well_height = self.vdd_position.y - inverter_well_ypos \
|
||||||
+ self.nwell_enclose_active + drc["minwidth_tx"]
|
+ self.nwell_enclose_active + drc["minwidth_tx"]
|
||||||
|
|
||||||
# FIXME fudge factor xpos
|
offset = [inverter_well_xpos, inverter_well_ypos]
|
||||||
offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos]
|
|
||||||
self.add_rect(layer="nwell",
|
self.add_rect(layer="nwell",
|
||||||
offset=offset,
|
offset=offset,
|
||||||
width=well_width,
|
width=well_width,
|
||||||
|
|
|
||||||
|
|
@ -86,10 +86,10 @@ class bank(design.design):
|
||||||
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
|
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
self.add_pin("din{0}_{1}".format(port,bit), "INPUT")
|
self.add_pin("din{0}_{1}".format(port, bit), "INPUT")
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
for bit in range(self.addr_size):
|
for bit in range(self.addr_size):
|
||||||
self.add_pin("addr{0}_{1}".format(port,bit), "INPUT")
|
self.add_pin("addr{0}_{1}".format(port, bit), "INPUT")
|
||||||
|
|
||||||
# For more than one bank, we have a bank select and name
|
# For more than one bank, we have a bank select and name
|
||||||
# the signals gated_*.
|
# the signals gated_*.
|
||||||
|
|
@ -133,16 +133,14 @@ class bank(design.design):
|
||||||
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
|
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
|
||||||
# This will ensure the pin is only on the top or bottom edge
|
# This will ensure the pin is only on the top or bottom edge
|
||||||
if port % 2:
|
if port % 2:
|
||||||
via_offset = bl_pin.uc() + vector(0, self.m2_pitch)
|
via_offset = bl_pin.uc() + vector(0, 1.5 * self.m2_pitch)
|
||||||
left_right_offset = vector(self.max_x_offset, via_offset.y)
|
left_right_offset = vector(self.max_x_offset, via_offset.y)
|
||||||
else:
|
else:
|
||||||
via_offset = bl_pin.bc() - vector(0, self.m2_pitch)
|
via_offset = bl_pin.bc() - vector(0, 1.5 * self.m2_pitch)
|
||||||
left_right_offset = vector(self.min_x_offset, via_offset.y)
|
left_right_offset = vector(self.min_x_offset, via_offset.y)
|
||||||
if bl_pin == "m1":
|
self.add_via_stack_center(from_layer=bl_pin.layer,
|
||||||
self.add_via_center(layers=self.m1_stack,
|
to_layer="m3",
|
||||||
offset=via_offset)
|
offset=via_offset)
|
||||||
self.add_via_center(layers=self.m2_stack,
|
|
||||||
offset=via_offset)
|
|
||||||
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
|
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
|
||||||
layer="m3",
|
layer="m3",
|
||||||
start=left_right_offset,
|
start=left_right_offset,
|
||||||
|
|
@ -643,11 +641,10 @@ class bank(design.design):
|
||||||
# Port 0
|
# Port 0
|
||||||
# The bank is at (0,0), so this is to the left of the y-axis.
|
# The bank is at (0,0), so this is to the left of the y-axis.
|
||||||
# 2 pitches on the right for vias/jogs to access the inputs
|
# 2 pitches on the right for vias/jogs to access the inputs
|
||||||
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset)
|
control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - self.m3_pitch, self.min_y_offset)
|
||||||
# The control bus is routed up to two pitches below the bitcell array
|
# The control bus is routed up to two pitches below the bitcell array
|
||||||
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
|
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
|
||||||
self.bus_xoffset[0] = self.create_bus(layer="m2",
|
self.bus_xoffset[0] = self.create_bus(layer="m2",
|
||||||
pitch=self.m2_pitch,
|
|
||||||
offset=control_bus_offset,
|
offset=control_bus_offset,
|
||||||
names=self.control_signals[0],
|
names=self.control_signals[0],
|
||||||
length=control_bus_length,
|
length=control_bus_length,
|
||||||
|
|
@ -658,11 +655,10 @@ class bank(design.design):
|
||||||
if len(self.all_ports)==2:
|
if len(self.all_ports)==2:
|
||||||
# The other control bus is routed up to two pitches above the bitcell array
|
# The other control bus is routed up to two pitches above the bitcell array
|
||||||
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
|
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
|
||||||
control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch,
|
control_bus_offset = vector(self.bitcell_array_right + self.m3_pitch,
|
||||||
self.max_y_offset - control_bus_length)
|
self.max_y_offset - control_bus_length)
|
||||||
# The bus for the right port is reversed so that the rbl_wl is closest to the array
|
# The bus for the right port is reversed so that the rbl_wl is closest to the array
|
||||||
self.bus_xoffset[1] = self.create_bus(layer="m2",
|
self.bus_xoffset[1] = self.create_bus(layer="m2",
|
||||||
pitch=self.m2_pitch,
|
|
||||||
offset=control_bus_offset,
|
offset=control_bus_offset,
|
||||||
names=list(reversed(self.control_signals[1])),
|
names=list(reversed(self.control_signals[1])),
|
||||||
length=control_bus_length,
|
length=control_bus_length,
|
||||||
|
|
@ -862,9 +858,9 @@ class bank(design.design):
|
||||||
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||||
|
|
||||||
if port % 2:
|
if port % 2:
|
||||||
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_pitch, 0)
|
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_nonpref_pitch, 0)
|
||||||
else:
|
else:
|
||||||
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
offset = self.column_decoder_inst[port].lr() + vector(self.m2_nonpref_pitch, 0)
|
||||||
|
|
||||||
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
|
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
|
||||||
|
|
||||||
|
|
@ -872,7 +868,9 @@ class bank(design.design):
|
||||||
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
|
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
|
||||||
|
|
||||||
route_map = list(zip(decode_pins, column_mux_pins))
|
route_map = list(zip(decode_pins, column_mux_pins))
|
||||||
self.create_vertical_channel_route(route_map, offset, self.m1_stack)
|
self.create_vertical_channel_route(route_map,
|
||||||
|
offset,
|
||||||
|
self.m1_stack)
|
||||||
|
|
||||||
def add_lvs_correspondence_points(self):
|
def add_lvs_correspondence_points(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -928,30 +926,39 @@ class bank(design.design):
|
||||||
# pre-decoder and this connection is in metal3
|
# pre-decoder and this connection is in metal3
|
||||||
connection = []
|
connection = []
|
||||||
connection.append((self.prefix + "p_en_bar{}".format(port),
|
connection.append((self.prefix + "p_en_bar{}".format(port),
|
||||||
self.port_data_inst[port].get_pin("p_en_bar").lc()))
|
self.port_data_inst[port].get_pin("p_en_bar").lc(),
|
||||||
|
self.port_data_inst[port].get_pin("p_en_bar").layer))
|
||||||
|
|
||||||
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
|
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
|
||||||
connection.append((self.prefix + "wl_en{}".format(port),
|
connection.append((self.prefix + "wl_en{}".format(port),
|
||||||
self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
|
self.bitcell_array_inst.get_pin(rbl_wl_name).lc(),
|
||||||
|
self.bitcell_array_inst.get_pin(rbl_wl_name).layer))
|
||||||
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
if port % 2:
|
if port % 2:
|
||||||
connection.append((self.prefix + "w_en{}".format(port),
|
connection.append((self.prefix + "w_en{}".format(port),
|
||||||
self.port_data_inst[port].get_pin("w_en").rc()))
|
self.port_data_inst[port].get_pin("w_en").rc(),
|
||||||
|
self.port_data_inst[port].get_pin("w_en").layer))
|
||||||
else:
|
else:
|
||||||
connection.append((self.prefix + "w_en{}".format(port),
|
connection.append((self.prefix + "w_en{}".format(port),
|
||||||
self.port_data_inst[port].get_pin("w_en").lc()))
|
self.port_data_inst[port].get_pin("w_en").lc(),
|
||||||
|
self.port_data_inst[port].get_pin("w_en").layer))
|
||||||
|
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
connection.append((self.prefix + "s_en{}".format(port),
|
connection.append((self.prefix + "s_en{}".format(port),
|
||||||
self.port_data_inst[port].get_pin("s_en").lc()))
|
self.port_data_inst[port].get_pin("s_en").lc(),
|
||||||
|
self.port_data_inst[port].get_pin("s_en").layer))
|
||||||
|
|
||||||
for (control_signal, pin_pos) in connection:
|
for (control_signal, pin_pos, pin_layer) in connection:
|
||||||
control_mid_pos = self.bus_xoffset[port][control_signal]
|
if port==0:
|
||||||
control_pos = vector(self.bus_xoffset[port][control_signal].x, pin_pos.y)
|
y_offset = self.min_y_offset
|
||||||
self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos])
|
else:
|
||||||
self.add_via_center(layers=self.m1_stack,
|
y_offset = self.max_y_offset
|
||||||
offset=control_pos)
|
control_pos = vector(self.bus_xoffset[port][control_signal].x, y_offset)
|
||||||
|
if pin_layer == "m1":
|
||||||
|
self.add_wire(self.m1_stack, [control_pos, pin_pos])
|
||||||
|
elif pin_layer == "m3":
|
||||||
|
self.add_wire(self.m2_stack[::-1], [control_pos, pin_pos])
|
||||||
|
|
||||||
# clk to wordline_driver
|
# clk to wordline_driver
|
||||||
control_signal = self.prefix + "wl_en{}".format(port)
|
control_signal = self.prefix + "wl_en{}".format(port)
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@ class bank_select(design.design):
|
||||||
bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint)
|
bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint)
|
||||||
self.add_path("m2", [bank_sel_line_pos, bank_sel_line_end])
|
self.add_path("m2", [bank_sel_line_pos, bank_sel_line_end])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=bank_sel_inv_pin.lc())
|
offset=bank_sel_inv_pin.center())
|
||||||
|
|
||||||
# Route the pin to the left edge as well
|
# Route the pin to the left edge as well
|
||||||
bank_sel_pin_pos=vector(0, 0)
|
bank_sel_pin_pos=vector(0, 0)
|
||||||
|
|
@ -242,30 +242,31 @@ class bank_select(design.design):
|
||||||
|
|
||||||
# Connect the logic output to inverter input
|
# Connect the logic output to inverter input
|
||||||
out_pin = logic_inst.get_pin("Z")
|
out_pin = logic_inst.get_pin("Z")
|
||||||
out_pos = out_pin.rc()
|
out_pos = out_pin.center()
|
||||||
in_pin = inv_inst.get_pin("A")
|
in_pin = inv_inst.get_pin("A")
|
||||||
in_pos = in_pin.lc()
|
in_pos = in_pin.center()
|
||||||
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
|
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
|
||||||
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
|
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
|
||||||
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
|
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
|
||||||
|
|
||||||
# Connect the logic B input to bank_sel / bank_sel_bar
|
# Connect the logic B input to bank_sel / bank_sel_bar
|
||||||
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5 * contact.m1_via.height, 0)
|
logic_pin = logic_inst.get_pin("B")
|
||||||
|
logic_pos = logic_pin.center()
|
||||||
input_pos = vector(xoffset_bank_signal, logic_pos.y)
|
input_pos = vector(xoffset_bank_signal, logic_pos.y)
|
||||||
self.add_path("m2", [logic_pos, input_pos])
|
self.add_path("m3", [logic_pos, input_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(self.m2_stack,
|
||||||
offset=logic_pos,
|
input_pos)
|
||||||
directions=("H", "H"))
|
self.add_via_stack_center(from_layer=logic_pin.layer,
|
||||||
|
to_layer="m3",
|
||||||
|
offset=logic_pos)
|
||||||
|
|
||||||
# Connect the logic A input to the input pin
|
# Connect the logic A input to the input pin
|
||||||
logic_pos = logic_inst.get_pin("A").lc()
|
logic_pin = logic_inst.get_pin("A")
|
||||||
|
logic_pos = logic_pin.center()
|
||||||
input_pos = vector(0, logic_pos.y)
|
input_pos = vector(0, logic_pos.y)
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_stack_center(from_layer=logic_pin.layer,
|
||||||
offset=logic_pos,
|
to_layer="m3",
|
||||||
directions=("H", "H"))
|
offset=logic_pos)
|
||||||
self.add_via_center(layers=self.m2_stack,
|
|
||||||
offset=logic_pos,
|
|
||||||
directions=("H", "H"))
|
|
||||||
self.add_layout_pin_segment_center(text=input_name,
|
self.add_layout_pin_segment_center(text=input_name,
|
||||||
layer="m3",
|
layer="m3",
|
||||||
start=input_pos,
|
start=input_pos,
|
||||||
|
|
|
||||||
|
|
@ -104,9 +104,9 @@ class bitcell_base_array(design.design):
|
||||||
# For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space
|
# For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space
|
||||||
# Default uses prefered directions for each layer; this cell property is only currently used by s8 tech (03/20)
|
# Default uses prefered directions for each layer; this cell property is only currently used by s8 tech (03/20)
|
||||||
try:
|
try:
|
||||||
force_power_pins_vertical = cell_properties.bitcell_force_power_pins_vertical
|
bitcell_power_pin_directions = cell_properties.bitcell_power_pin_directions
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
force_power_pins_vertical = None
|
bitcell_power_pin_directions = None
|
||||||
|
|
||||||
# Add vdd/gnd via stacks
|
# Add vdd/gnd via stacks
|
||||||
for row in range(self.row_size):
|
for row in range(self.row_size):
|
||||||
|
|
@ -114,7 +114,10 @@ class bitcell_base_array(design.design):
|
||||||
inst = self.cell_inst[row,col]
|
inst = self.cell_inst[row,col]
|
||||||
for pin_name in ["vdd", "gnd"]:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
for pin in inst.get_pins(pin_name):
|
for pin in inst.get_pins(pin_name):
|
||||||
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=force_power_pins_vertical, start_layer=pin.layer)
|
self.add_power_pin(name=pin_name,
|
||||||
|
loc=pin.center(),
|
||||||
|
directions=bitcell_power_pin_directions,
|
||||||
|
start_layer=pin.layer)
|
||||||
|
|
||||||
def _adjust_x_offset(self, xoffset, col, col_offset):
|
def _adjust_x_offset(self, xoffset, col, col_offset):
|
||||||
tempx = xoffset
|
tempx = xoffset
|
||||||
|
|
|
||||||
|
|
@ -384,7 +384,10 @@ class control_logic(design.design):
|
||||||
height = self.control_logic_center.y - self.m2_pitch
|
height = self.control_logic_center.y - self.m2_pitch
|
||||||
offset = vector(self.ctrl_dff_array.width, 0)
|
offset = vector(self.ctrl_dff_array.width, 0)
|
||||||
|
|
||||||
self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height)
|
self.rail_offsets = self.create_vertical_bus("m2",
|
||||||
|
offset,
|
||||||
|
self.internal_bus_list,
|
||||||
|
height)
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create all the instances """
|
""" Create all the instances """
|
||||||
|
|
@ -522,16 +525,8 @@ class control_logic(design.design):
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=clk_pos)
|
offset=clk_pos)
|
||||||
|
|
||||||
# Connect this at the bottom of the buffer
|
self.route_output_to_bus_jogged(self.clk_buf_inst,
|
||||||
out_pos = self.clk_buf_inst.get_pin("Z").center()
|
"clk_buf")
|
||||||
mid1 = vector(out_pos.x, 2 * self.m2_pitch)
|
|
||||||
mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y)
|
|
||||||
bus_pos = self.rail_offsets["clk_buf"]
|
|
||||||
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
|
|
||||||
# The pin is on M1, so we need another via as well
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=self.clk_buf_inst.get_pin("Z").center())
|
|
||||||
|
|
||||||
self.connect_output(self.clk_buf_inst, "Z", "clk_buf")
|
self.connect_output(self.clk_buf_inst, "Z", "clk_buf")
|
||||||
|
|
||||||
def create_gated_clk_bar_row(self):
|
def create_gated_clk_bar_row(self):
|
||||||
|
|
@ -541,7 +536,7 @@ class control_logic(design.design):
|
||||||
|
|
||||||
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
|
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
|
||||||
mod=self.and2)
|
mod=self.and2)
|
||||||
self.connect_inst(["cs", "clk_bar", "gated_clk_bar", "vdd", "gnd"])
|
self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_gated_clk_bar_row(self, row):
|
def place_gated_clk_bar_row(self, row):
|
||||||
x_offset = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
|
|
@ -556,29 +551,24 @@ class control_logic(design.design):
|
||||||
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
|
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
|
||||||
|
|
||||||
out_pos = self.clk_bar_inst.get_pin("Z").center()
|
out_pos = self.clk_bar_inst.get_pin("Z").center()
|
||||||
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
|
in_pos = self.gated_clk_bar_inst.get_pin("A").center()
|
||||||
mid1 = vector(in_pos.x, out_pos.y)
|
self.add_zjog("m1", out_pos, in_pos)
|
||||||
self.add_path("m1", [out_pos, mid1, in_pos])
|
|
||||||
|
|
||||||
# This is the second gate over, so it needs to be on M3
|
# This is the second gate over, so it needs to be on M3
|
||||||
clkbuf_map = zip(["A"], ["cs"])
|
clkbuf_map = zip(["B"], ["cs"])
|
||||||
self.connect_vertical_bus(clkbuf_map,
|
self.connect_vertical_bus(clkbuf_map,
|
||||||
self.gated_clk_bar_inst,
|
self.gated_clk_bar_inst,
|
||||||
self.rail_offsets,
|
self.rail_offsets,
|
||||||
self.m2_stack[::-1])
|
self.m2_stack[::-1])
|
||||||
# The pin is on M1, so we need another via as well
|
# The pin is on M1, so we need another via as well
|
||||||
self.add_via_center(layers=self.m1_stack,
|
b_pin = self.gated_clk_bar_inst.get_pin("B")
|
||||||
offset=self.gated_clk_bar_inst.get_pin("A").center())
|
self.add_via_stack_center(from_layer=b_pin.layer,
|
||||||
|
to_layer="m3",
|
||||||
|
offset=b_pin.center())
|
||||||
|
|
||||||
# This is the second gate over, so it needs to be on M3
|
# This is the second gate over, so it needs to be on M3
|
||||||
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
|
self.route_output_to_bus_jogged(self.gated_clk_bar_inst,
|
||||||
self.connect_vertical_bus(clkbuf_map,
|
"gated_clk_bar")
|
||||||
self.gated_clk_bar_inst,
|
|
||||||
self.rail_offsets,
|
|
||||||
self.m2_stack[::-1])
|
|
||||||
# The pin is on M1, so we need another via as well
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=self.gated_clk_bar_inst.get_pin("Z").center())
|
|
||||||
|
|
||||||
def create_gated_clk_buf_row(self):
|
def create_gated_clk_buf_row(self):
|
||||||
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
|
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
|
||||||
|
|
@ -594,7 +584,9 @@ class control_logic(design.design):
|
||||||
|
|
||||||
def route_gated_clk_buf(self):
|
def route_gated_clk_buf(self):
|
||||||
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
|
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
|
||||||
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
|
self.connect_vertical_bus(clkbuf_map,
|
||||||
|
self.gated_clk_buf_inst,
|
||||||
|
self.rail_offsets)
|
||||||
|
|
||||||
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
|
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
|
||||||
self.connect_vertical_bus(clkbuf_map,
|
self.connect_vertical_bus(clkbuf_map,
|
||||||
|
|
@ -602,8 +594,10 @@ class control_logic(design.design):
|
||||||
self.rail_offsets,
|
self.rail_offsets,
|
||||||
self.m2_stack[::-1])
|
self.m2_stack[::-1])
|
||||||
# The pin is on M1, so we need another via as well
|
# The pin is on M1, so we need another via as well
|
||||||
self.add_via_center(layers=self.m1_stack,
|
z_pin = self.gated_clk_buf_inst.get_pin("Z")
|
||||||
offset=self.gated_clk_buf_inst.get_pin("Z").center())
|
self.add_via_stack_center(from_layer=z_pin.layer,
|
||||||
|
to_layer="m2",
|
||||||
|
offset=z_pin.center())
|
||||||
|
|
||||||
def create_wlen_row(self):
|
def create_wlen_row(self):
|
||||||
# input pre_p_en, output: wl_en
|
# input pre_p_en, output: wl_en
|
||||||
|
|
@ -647,10 +641,15 @@ class control_logic(design.design):
|
||||||
in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"])
|
in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"])
|
||||||
self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.rail_offsets)
|
self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.rail_offsets)
|
||||||
|
|
||||||
out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc()
|
out_pin = self.p_en_bar_nand_inst.get_pin("Z")
|
||||||
in_pos = self.p_en_bar_driver_inst.get_pin("A").lc()
|
out_pos = out_pin.center()
|
||||||
mid1 = vector(out_pos.x, in_pos.y)
|
in_pin = self.p_en_bar_driver_inst.get_pin("A")
|
||||||
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
|
in_pos = in_pin.center()
|
||||||
|
mid1 = vector(in_pos.x, out_pos.y)
|
||||||
|
self.add_path(out_pin.layer, [out_pos, mid1, in_pos])
|
||||||
|
self.add_via_stack_center(from_layer=out_pin.layer,
|
||||||
|
to_layer=in_pin.layer,
|
||||||
|
offset=in_pin.center())
|
||||||
|
|
||||||
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
|
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
|
||||||
|
|
||||||
|
|
@ -704,11 +703,7 @@ class control_logic(design.design):
|
||||||
# Connect from delay line
|
# Connect from delay line
|
||||||
# Connect to rail
|
# Connect to rail
|
||||||
|
|
||||||
rbl_map = zip(["Z"], ["rbl_bl_delay_bar"])
|
self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar")
|
||||||
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("m3", "via2", "m2"))
|
|
||||||
# The pin is on M1, so we need another via as well
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center())
|
|
||||||
|
|
||||||
rbl_map = zip(["A"], ["rbl_bl_delay"])
|
rbl_map = zip(["A"], ["rbl_bl_delay"])
|
||||||
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
|
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
|
||||||
|
|
@ -766,11 +761,12 @@ class control_logic(design.design):
|
||||||
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
|
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
|
||||||
else:
|
else:
|
||||||
dff_out_map = zip(["dout_bar_0"], ["cs"])
|
dff_out_map = zip(["dout_bar_0"], ["cs"])
|
||||||
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("m3", "via2", "m2"))
|
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, self.m2_stack[::-1])
|
||||||
|
|
||||||
# Connect the clock rail to the other clock rail
|
# Connect the clock rail to the other clock rail
|
||||||
|
# by routing in the supply rail track to avoid channel conflicts
|
||||||
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
|
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
|
||||||
mid_pos = in_pos + vector(0, 2 * self.m2_pitch)
|
mid_pos = in_pos + vector(0, self.and2.height)
|
||||||
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
|
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
|
||||||
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
|
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
|
|
@ -1000,3 +996,15 @@ class control_logic(design.design):
|
||||||
offset = vector(x_offset, y_offset)
|
offset = vector(x_offset, y_offset)
|
||||||
inst.place(offset, mirror)
|
inst.place(offset, mirror)
|
||||||
return x_offset + inst.width
|
return x_offset + inst.width
|
||||||
|
|
||||||
|
def route_output_to_bus_jogged(self, inst, name):
|
||||||
|
# Connect this at the bottom of the buffer
|
||||||
|
out_pos = inst.get_pin("Z").center()
|
||||||
|
mid1 = vector(out_pos.x, out_pos.y - 0.25 * inst.mod.height)
|
||||||
|
mid2 = vector(self.rail_offsets[name].x, mid1.y)
|
||||||
|
bus_pos = self.rail_offsets[name]
|
||||||
|
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
|
||||||
|
# The pin is on M1, so we need another via as well
|
||||||
|
self.add_via_center(layers=self.m1_stack,
|
||||||
|
offset=out_pos)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ class delay_chain(design.design):
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.inv = factory.create(module_type="pinv", route_output=False)
|
self.inv = factory.create(module_type="pinv")
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
def create_inverters(self):
|
def create_inverters(self):
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ class dff_buf(design.design):
|
||||||
height=din_pin.height())
|
height=din_pin.height())
|
||||||
|
|
||||||
dout_pin = self.inv2_inst.get_pin("Z")
|
dout_pin = self.inv2_inst.get_pin("Z")
|
||||||
mid_pos = dout_pin.center() + vector(self.m1_pitch, 0)
|
mid_pos = dout_pin.center() + vector(self.m1_nonpref_pitch, 0)
|
||||||
q_pos = mid_pos - vector(0, self.m2_pitch)
|
q_pos = mid_pos - vector(0, self.m2_pitch)
|
||||||
self.add_layout_pin_rect_center(text="Q",
|
self.add_layout_pin_rect_center(text="Q",
|
||||||
layer="m2",
|
layer="m2",
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from errors import drc_error
|
from errors import drc_error
|
||||||
from tech import cell_properties
|
from tech import cell_properties, layer
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_decoder(design.design):
|
class hierarchical_decoder(design.design):
|
||||||
|
|
@ -89,6 +89,7 @@ class hierarchical_decoder(design.design):
|
||||||
self.place_pre_decoder()
|
self.place_pre_decoder()
|
||||||
self.place_row_decoder()
|
self.place_row_decoder()
|
||||||
self.route_inputs()
|
self.route_inputs()
|
||||||
|
self.route_outputs()
|
||||||
self.route_decoder_bus()
|
self.route_decoder_bus()
|
||||||
self.route_vdd_gnd()
|
self.route_vdd_gnd()
|
||||||
self.offset_all_coordinates()
|
self.offset_all_coordinates()
|
||||||
|
|
@ -103,7 +104,7 @@ class hierarchical_decoder(design.design):
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and2)
|
self.add_mod(self.and2)
|
||||||
self.and3 = factory.create(module_type="pand3",
|
self.and3 = factory.create(module_type="pand3",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and3)
|
self.add_mod(self.and3)
|
||||||
|
|
||||||
self.add_decoders()
|
self.add_decoders()
|
||||||
|
|
@ -186,19 +187,36 @@ class hierarchical_decoder(design.design):
|
||||||
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
|
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
|
||||||
# We will place this many final decoders per row
|
# We will place this many final decoders per row
|
||||||
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
|
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
|
||||||
|
# We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row
|
||||||
|
if self.decoders_per_row == 1:
|
||||||
|
self.decoder_bus_pitch = self.m2_pitch
|
||||||
|
elif self.decoders_per_row == 2:
|
||||||
|
self.decoder_bus_pitch = self.m3_pitch
|
||||||
|
else:
|
||||||
|
debug.error("Insufficient layers for multi-bit height decoder.", -1)
|
||||||
|
|
||||||
# Calculates height and width of row-decoder
|
# Calculates height and width of row-decoder
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
nand_width = self.and2.width
|
nand_width = self.and2.width
|
||||||
|
nand_inputs = 2
|
||||||
else:
|
else:
|
||||||
nand_width = self.and3.width
|
nand_width = self.and3.width
|
||||||
self.internal_routing_width = self.m2_pitch * (self.total_number_of_predecoder_outputs + 1)
|
nand_inputs = 3
|
||||||
|
self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1)
|
||||||
self.row_decoder_height = self.inv.height * self.num_rows
|
self.row_decoder_height = self.inv.height * self.num_rows
|
||||||
|
|
||||||
|
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch
|
||||||
|
# print(self.decoders_per_row, nand_inputs)
|
||||||
|
# print(decoder_input_wire_height, self.cell_height)
|
||||||
|
if decoder_input_wire_height > self.cell_height:
|
||||||
|
debug.warning("Cannot fit multi-bit decoder routes per row.")
|
||||||
|
# debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
|
||||||
|
|
||||||
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
||||||
|
|
||||||
# Calculates height and width of hierarchical decoder
|
# Calculates height and width of hierarchical decoder
|
||||||
# Add extra pitch for good measure
|
# Add extra pitch for good measure
|
||||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch
|
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch
|
||||||
self.width = self.input_routing_width + self.predecoder_width \
|
self.width = self.input_routing_width + self.predecoder_width \
|
||||||
+ self.internal_routing_width \
|
+ self.internal_routing_width \
|
||||||
+ self.decoders_per_row * nand_width + self.inv.width
|
+ self.decoders_per_row * nand_width + self.inv.width
|
||||||
|
|
@ -218,7 +236,6 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
||||||
self.input_bus = self.create_vertical_pin_bus(layer="m2",
|
self.input_bus = self.create_vertical_pin_bus(layer="m2",
|
||||||
pitch=self.m2_pitch,
|
|
||||||
offset=input_offset,
|
offset=input_offset,
|
||||||
names=input_bus_names,
|
names=input_bus_names,
|
||||||
length=input_height)
|
length=input_height)
|
||||||
|
|
@ -238,7 +255,7 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
# To prevent conflicts, we will offset each input connect so
|
# To prevent conflicts, we will offset each input connect so
|
||||||
# that it aligns with the vdd/gnd rails
|
# that it aligns with the vdd/gnd rails
|
||||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_bus(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
@ -254,7 +271,7 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
# To prevent conflicts, we will offset each input connect so
|
# To prevent conflicts, we will offset each input connect so
|
||||||
# that it aligns with the vdd/gnd rails
|
# that it aligns with the vdd/gnd rails
|
||||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_bus(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
@ -265,11 +282,13 @@ class hierarchical_decoder(design.design):
|
||||||
vertical M2 coordinate to the predecode inputs
|
vertical M2 coordinate to the predecode inputs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_stack_center(from_layer="m2",
|
||||||
offset=input_offset)
|
to_layer="m3",
|
||||||
self.add_via_center(layers=self.m2_stack,
|
offset=input_offset)
|
||||||
offset=output_offset)
|
self.add_via_stack_center(from_layer="m2",
|
||||||
self.add_path(("m3"), [input_offset, output_offset])
|
to_layer="m3",
|
||||||
|
offset=output_offset)
|
||||||
|
self.add_path("m3", [input_offset, output_offset])
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" Add the module pins """
|
""" Add the module pins """
|
||||||
|
|
@ -411,7 +430,7 @@ class hierarchical_decoder(design.design):
|
||||||
"""
|
"""
|
||||||
if (self.num_inputs >= 4):
|
if (self.num_inputs >= 4):
|
||||||
self.place_decoder_and_array()
|
self.place_decoder_and_array()
|
||||||
self.route_decoder()
|
|
||||||
|
|
||||||
def place_decoder_and_array(self):
|
def place_decoder_and_array(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -447,16 +466,28 @@ class hierarchical_decoder(design.design):
|
||||||
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
|
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def route_decoder(self):
|
def route_outputs(self):
|
||||||
""" Add the pins. """
|
""" Add the pins. """
|
||||||
|
|
||||||
for output in range(self.num_outputs):
|
max_xoffset = max(x.rx() for x in self.and_inst)
|
||||||
z_pin = self.and_inst[output].get_pin("Z")
|
|
||||||
self.add_layout_pin(text="decode_{0}".format(output),
|
for output_index in range(self.num_outputs):
|
||||||
layer="m1",
|
row_remainder = (output_index % self.decoders_per_row)
|
||||||
offset=z_pin.ll(),
|
|
||||||
width=z_pin.width(),
|
and_inst = self.and_inst[output_index]
|
||||||
height=z_pin.height())
|
z_pin = and_inst.get_pin("Z")
|
||||||
|
if row_remainder == 0 and self.decoders_per_row > 1:
|
||||||
|
layer = "m3"
|
||||||
|
self.add_via_stack_center(from_layer=z_pin.layer,
|
||||||
|
to_layer="m3",
|
||||||
|
offset=z_pin.center())
|
||||||
|
else:
|
||||||
|
layer = z_pin.layer
|
||||||
|
|
||||||
|
self.add_layout_pin_segment_center(text="decode_{0}".format(output_index),
|
||||||
|
layer=layer,
|
||||||
|
start=z_pin.center(),
|
||||||
|
end=vector(max_xoffset, z_pin.cy()))
|
||||||
|
|
||||||
def route_decoder_bus(self):
|
def route_decoder_bus(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -468,7 +499,7 @@ class hierarchical_decoder(design.design):
|
||||||
# This leaves an offset for the predecoder output jogs
|
# This leaves an offset for the predecoder output jogs
|
||||||
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||||
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
|
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
|
||||||
pitch=self.m2_pitch,
|
pitch=self.decoder_bus_pitch,
|
||||||
offset=vector(0, 0),
|
offset=vector(0, 0),
|
||||||
names=input_bus_names,
|
names=input_bus_names,
|
||||||
length=self.height)
|
length=self.height)
|
||||||
|
|
@ -512,22 +543,27 @@ class hierarchical_decoder(design.design):
|
||||||
"""
|
"""
|
||||||
output_index = 0
|
output_index = 0
|
||||||
|
|
||||||
|
if "li" in layer:
|
||||||
|
self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]]
|
||||||
|
else:
|
||||||
|
self.decoder_layers = [self.m2_stack[::-1]]
|
||||||
|
debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.")
|
||||||
|
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
for index_B in self.predec_groups[1]:
|
for index_B in self.predec_groups[1]:
|
||||||
for index_A in self.predec_groups[0]:
|
for index_A in self.predec_groups[0]:
|
||||||
# FIXME: convert to connect_bus?
|
# FIXME: convert to connect_bus?
|
||||||
if (output_index < self.num_outputs):
|
if (output_index < self.num_outputs):
|
||||||
row_index = math.floor(output_index / self.decoders_per_row)
|
|
||||||
row_remainder = (output_index % self.decoders_per_row)
|
|
||||||
row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 1) * self.m3_pitch
|
|
||||||
predecode_name = "predecode_{}".format(index_A)
|
predecode_name = "predecode_{}".format(index_A)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("A"),
|
self.and_inst[output_index].get_pin("A"),
|
||||||
row_offset)
|
output_index,
|
||||||
|
0)
|
||||||
predecode_name = "predecode_{}".format(index_B)
|
predecode_name = "predecode_{}".format(index_B)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("B"),
|
self.and_inst[output_index].get_pin("B"),
|
||||||
row_offset + self.m3_pitch)
|
output_index,
|
||||||
|
1)
|
||||||
output_index = output_index + 1
|
output_index = output_index + 1
|
||||||
|
|
||||||
elif (self.num_inputs > 5):
|
elif (self.num_inputs > 5):
|
||||||
|
|
@ -536,21 +572,21 @@ class hierarchical_decoder(design.design):
|
||||||
for index_A in self.predec_groups[0]:
|
for index_A in self.predec_groups[0]:
|
||||||
# FIXME: convert to connect_bus?
|
# FIXME: convert to connect_bus?
|
||||||
if (output_index < self.num_outputs):
|
if (output_index < self.num_outputs):
|
||||||
row_index = math.floor(output_index / self.decoders_per_row)
|
|
||||||
row_remainder = (output_index % self.decoders_per_row)
|
|
||||||
row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 1) * self.m3_pitch
|
|
||||||
predecode_name = "predecode_{}".format(index_A)
|
predecode_name = "predecode_{}".format(index_A)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("A"),
|
self.and_inst[output_index].get_pin("A"),
|
||||||
row_offset)
|
output_index,
|
||||||
|
0)
|
||||||
predecode_name = "predecode_{}".format(index_B)
|
predecode_name = "predecode_{}".format(index_B)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("B"),
|
self.and_inst[output_index].get_pin("B"),
|
||||||
row_offset + self.m3_pitch)
|
output_index,
|
||||||
|
1)
|
||||||
predecode_name = "predecode_{}".format(index_C)
|
predecode_name = "predecode_{}".format(index_C)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("C"),
|
self.and_inst[output_index].get_pin("C"),
|
||||||
row_offset + 2 * self.m3_pitch)
|
output_index,
|
||||||
|
2)
|
||||||
output_index = output_index + 1
|
output_index = output_index + 1
|
||||||
|
|
||||||
def route_vdd_gnd(self):
|
def route_vdd_gnd(self):
|
||||||
|
|
@ -570,38 +606,55 @@ class hierarchical_decoder(design.design):
|
||||||
# The nand and inv are the same height rows...
|
# The nand and inv are the same height rows...
|
||||||
supply_pin = self.and_inst[num].get_pin(pin_name)
|
supply_pin = self.and_inst[num].get_pin(pin_name)
|
||||||
pin_pos = vector(xoffset, supply_pin.cy())
|
pin_pos = vector(xoffset, supply_pin.cy())
|
||||||
self.add_path("m1",
|
self.add_path(supply_pin.layer,
|
||||||
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
|
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
|
||||||
self.add_power_pin(name=pin_name,
|
self.add_power_pin(name=pin_name,
|
||||||
loc=pin_pos)
|
loc=pin_pos,
|
||||||
|
start_layer=supply_pin.layer)
|
||||||
|
|
||||||
# Copy the pins from the predecoders
|
# Copy the pins from the predecoders
|
||||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||||
self.copy_layout_pin(pre, "vdd")
|
self.copy_layout_pin(pre, "vdd")
|
||||||
self.copy_layout_pin(pre, "gnd")
|
self.copy_layout_pin(pre, "gnd")
|
||||||
|
|
||||||
def route_predecode_bus_outputs(self, rail_name, pin, y_offset):
|
def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index):
|
||||||
"""
|
"""
|
||||||
Connect the routing rail to the given metal1 pin
|
Connect the routing rail to the given metal1 pin
|
||||||
using a routing track at the given y_offset
|
using a routing track at the given y_offset
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
row_index = math.floor(output_index / self.decoders_per_row)
|
||||||
|
row_remainder = (output_index % self.decoders_per_row)
|
||||||
|
row_offset = row_index * self.and_inst[0].height
|
||||||
|
|
||||||
pin_pos = pin.center()
|
pin_pos = pin.center()
|
||||||
# If we have a single decoder per row, we can route on M1
|
|
||||||
if self.decoders_per_row == 1:
|
# y_offset is the same for both the M2 and M4 routes so that the rail
|
||||||
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
# contacts align and don't cause problems
|
||||||
self.add_path("m1", [rail_pos, pin_pos])
|
if pin_index == 0:
|
||||||
self.add_via_center(layers=self.m1_stack,
|
# Bottom pitch
|
||||||
offset=rail_pos)
|
y_offset = row_offset
|
||||||
# If not, we must route over the decoder cells on M3
|
elif pin_index == 1:
|
||||||
|
# One pitch from top
|
||||||
|
y_offset = row_offset + self.and_inst[0].height - self.m3_pitch
|
||||||
|
elif pin_index == 2:
|
||||||
|
# One pitch from bottom
|
||||||
|
y_offset = row_offset + self.m3_pitch
|
||||||
else:
|
else:
|
||||||
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
|
debug.error("Invalid decoder pitch.")
|
||||||
mid_pos = vector(pin_pos.x, rail_pos.y)
|
|
||||||
self.add_wire(self.m2_stack[::-1], [rail_pos, mid_pos, pin_pos])
|
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
|
||||||
self.add_via_center(layers=self.m2_stack,
|
mid_pos = vector(pin_pos.x, rail_pos.y)
|
||||||
offset=rail_pos)
|
self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=pin_pos)
|
self.add_via_stack_center(from_layer="m2",
|
||||||
|
to_layer=self.decoder_layers[row_remainder][0],
|
||||||
|
offset=rail_pos)
|
||||||
|
|
||||||
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
|
to_layer=self.decoder_layers[row_remainder][2],
|
||||||
|
offset=pin_pos,
|
||||||
|
directions=("H", "H"))
|
||||||
|
|
||||||
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
|
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
|
||||||
"""
|
"""
|
||||||
|
|
@ -610,13 +663,21 @@ class hierarchical_decoder(design.design):
|
||||||
"""
|
"""
|
||||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||||
# It would be fixed with a channel router.
|
# It would be fixed with a channel router.
|
||||||
|
# pin_pos = pin.center()
|
||||||
|
# mid_point1 = vector(x_offset, pin_pos.y)
|
||||||
|
# mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
|
||||||
|
# rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
||||||
|
# self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||||
|
|
||||||
pin_pos = pin.center()
|
pin_pos = pin.center()
|
||||||
mid_point1 = vector(x_offset, pin_pos.y)
|
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||||
mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
|
self.add_path("m1", [pin_pos, rail_pos])
|
||||||
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
self.add_wire(self.m1_stack, [pin_pos, mid_point1, mid_point2, rail_pos])
|
to_layer="m1",
|
||||||
self.add_via_center(layers=self.m1_stack,
|
offset=pin_pos)
|
||||||
offset=rail_pos)
|
self.add_via_stack_center(from_layer="m1",
|
||||||
|
to_layer="m2",
|
||||||
|
offset=rail_pos)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
if self.determine_predecodes(self.num_inputs)[1]==0:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import math
|
||||||
import contact
|
import contact
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
from tech import cell_properties
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_predecode(design.design):
|
class hierarchical_predecode(design.design):
|
||||||
|
|
@ -19,7 +20,17 @@ class hierarchical_predecode(design.design):
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, input_number, height=None):
|
def __init__(self, name, input_number, height=None):
|
||||||
self.number_of_inputs = input_number
|
self.number_of_inputs = input_number
|
||||||
self.cell_height = height
|
|
||||||
|
if not height:
|
||||||
|
b = factory.create(module_type="bitcell")
|
||||||
|
try:
|
||||||
|
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
||||||
|
except AttributeError:
|
||||||
|
self.cell_multiple = 1
|
||||||
|
self.cell_height = self.cell_multiple * b.height
|
||||||
|
else:
|
||||||
|
self.cell_height = height
|
||||||
|
|
||||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
|
@ -57,10 +68,10 @@ class hierarchical_predecode(design.design):
|
||||||
self.height = self.number_of_outputs * self.and_mod.height
|
self.height = self.number_of_outputs * self.and_mod.height
|
||||||
|
|
||||||
# x offset for input inverters
|
# x offset for input inverters
|
||||||
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
|
self.x_off_inv_1 = self.number_of_inputs * self.m2_pitch + self.m2_space
|
||||||
|
|
||||||
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
||||||
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
|
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.m2_pitch
|
||||||
|
|
||||||
# x offset to output inverters
|
# x offset to output inverters
|
||||||
self.width = self.x_off_and + self.and_mod.width
|
self.width = self.x_off_and + self.and_mod.width
|
||||||
|
|
@ -68,9 +79,8 @@ class hierarchical_predecode(design.design):
|
||||||
def route_rails(self):
|
def route_rails(self):
|
||||||
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
||||||
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
|
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
|
||||||
offset = vector(0.5 * self.m2_width, self.m1_pitch)
|
offset = vector(0.5 * self.m2_width, self.m3_pitch)
|
||||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
||||||
pitch=self.m2_pitch,
|
|
||||||
offset=offset,
|
offset=offset,
|
||||||
names=input_names,
|
names=input_names,
|
||||||
length=self.height - 2 * self.m1_pitch)
|
length=self.height - 2 * self.m1_pitch)
|
||||||
|
|
@ -78,9 +88,8 @@ class hierarchical_predecode(design.design):
|
||||||
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
|
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
|
||||||
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
|
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
|
||||||
decode_names = invert_names + non_invert_names
|
decode_names = invert_names + non_invert_names
|
||||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m1_pitch)
|
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m3_pitch)
|
||||||
self.decode_rails = self.create_vertical_bus(layer="m2",
|
self.decode_rails = self.create_vertical_bus(layer="m2",
|
||||||
pitch=self.m2_pitch,
|
|
||||||
offset=offset,
|
offset=offset,
|
||||||
names=decode_names,
|
names=decode_names,
|
||||||
length=self.height - 2 * self.m1_pitch)
|
length=self.height - 2 * self.m1_pitch)
|
||||||
|
|
@ -146,16 +155,18 @@ class hierarchical_predecode(design.design):
|
||||||
# route one signal next to each vdd/gnd rail since this is
|
# route one signal next to each vdd/gnd rail since this is
|
||||||
# typically where the p/n devices are and there are no
|
# typically where the p/n devices are and there are no
|
||||||
# pins in the and gates.
|
# pins in the and gates.
|
||||||
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
|
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m2_via.width + self.m2_space
|
||||||
in_pin = "in_{}".format(num)
|
in_pin = "in_{}".format(num)
|
||||||
a_pin = "A_{}".format(num)
|
a_pin = "A_{}".format(num)
|
||||||
in_pos = vector(self.input_rails[in_pin].x, y_offset)
|
in_pos = vector(self.input_rails[in_pin].x, y_offset)
|
||||||
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
|
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
|
||||||
self.add_path("m1", [in_pos, a_pos])
|
self.add_path("m1", [in_pos, a_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_stack_center(from_layer="m1",
|
||||||
offset=[self.input_rails[in_pin].x, y_offset])
|
to_layer="m2",
|
||||||
self.add_via_center(layers=self.m1_stack,
|
offset=[self.input_rails[in_pin].x, y_offset])
|
||||||
offset=[self.decode_rails[a_pin].x, y_offset])
|
self.add_via_stack_center(from_layer="m1",
|
||||||
|
to_layer="m2",
|
||||||
|
offset=[self.decode_rails[a_pin].x, y_offset])
|
||||||
|
|
||||||
def route_output_and(self):
|
def route_output_and(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -165,7 +176,7 @@ class hierarchical_predecode(design.design):
|
||||||
|
|
||||||
z_pin = self.and_inst[num].get_pin("Z")
|
z_pin = self.and_inst[num].get_pin("Z")
|
||||||
self.add_layout_pin(text="out_{}".format(num),
|
self.add_layout_pin(text="out_{}".format(num),
|
||||||
layer="m1",
|
layer=z_pin.layer,
|
||||||
offset=z_pin.ll(),
|
offset=z_pin.ll(),
|
||||||
height=z_pin.height(),
|
height=z_pin.height(),
|
||||||
width=z_pin.width())
|
width=z_pin.width())
|
||||||
|
|
@ -178,24 +189,30 @@ class hierarchical_predecode(design.design):
|
||||||
out_pin = "Abar_{}".format(inv_num)
|
out_pin = "Abar_{}".format(inv_num)
|
||||||
in_pin = "in_{}".format(inv_num)
|
in_pin = "in_{}".format(inv_num)
|
||||||
|
|
||||||
#add output so that it is just below the vdd or gnd rail
|
# add output so that it is just below the vdd or gnd rail
|
||||||
# since this is where the p/n devices are and there are no
|
# since this is where the p/n devices are and there are no
|
||||||
# pins in the and gates.
|
# pins in the and gates.
|
||||||
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
|
y_offset = (inv_num + 1) * self.inv.height - 3 * self.m1_space
|
||||||
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
|
inv_out_pin = self.in_inst[inv_num].get_pin("Z")
|
||||||
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
|
inv_out_pos = inv_out_pin.rc()
|
||||||
rail_pos = vector(self.decode_rails[out_pin].x,y_offset)
|
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(), 0)
|
||||||
self.add_path("m1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
rail_pos = vector(self.decode_rails[out_pin].x, y_offset)
|
||||||
self.add_via_center(layers = self.m1_stack,
|
self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
||||||
offset=rail_pos)
|
self.add_via_stack_center(from_layer=inv_out_pin.layer,
|
||||||
|
to_layer="m2",
|
||||||
|
offset=rail_pos)
|
||||||
|
|
||||||
|
# route input
|
||||||
#route input
|
pin = self.in_inst[inv_num].get_pin("A")
|
||||||
inv_in_pos = self.in_inst[inv_num].get_pin("A").lc()
|
inv_in_pos = pin.lc()
|
||||||
in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y)
|
in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y)
|
||||||
self.add_path("m1", [in_pos, inv_in_pos])
|
self.add_path("m1", [in_pos, inv_in_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
offset=in_pos)
|
to_layer="m1",
|
||||||
|
offset=inv_in_pos)
|
||||||
|
self.add_via_stack_center(from_layer="m1",
|
||||||
|
to_layer="m2",
|
||||||
|
offset=in_pos)
|
||||||
|
|
||||||
def route_and_to_rails(self):
|
def route_and_to_rails(self):
|
||||||
# This 2D array defines the connection mapping
|
# This 2D array defines the connection mapping
|
||||||
|
|
@ -205,17 +222,28 @@ class hierarchical_predecode(design.design):
|
||||||
index_lst= and_input_line_combination[k]
|
index_lst= and_input_line_combination[k]
|
||||||
|
|
||||||
if self.number_of_inputs == 2:
|
if self.number_of_inputs == 2:
|
||||||
gate_lst = ["A","B"]
|
gate_lst = ["A", "B"]
|
||||||
else:
|
else:
|
||||||
gate_lst = ["A","B","C"]
|
gate_lst = ["A", "B", "C"]
|
||||||
|
|
||||||
# this will connect pins A,B or A,B,C
|
# this will connect pins A,B or A,B,C
|
||||||
for rail_pin,gate_pin in zip(index_lst,gate_lst):
|
for rail_pin, gate_pin in zip(index_lst, gate_lst):
|
||||||
pin_pos = self.and_inst[k].get_pin(gate_pin).lc()
|
pin = self.and_inst[k].get_pin(gate_pin)
|
||||||
|
pin_pos = pin.center()
|
||||||
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
||||||
self.add_path("m1", [rail_pos, pin_pos])
|
self.add_path("m1", [rail_pos, pin_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_stack_center(from_layer="m1",
|
||||||
offset=rail_pos)
|
to_layer="m2",
|
||||||
|
offset=rail_pos)
|
||||||
|
if gate_pin == "A":
|
||||||
|
direction = None
|
||||||
|
else:
|
||||||
|
direction = ("H", "H")
|
||||||
|
|
||||||
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
|
to_layer="m1",
|
||||||
|
offset=pin_pos,
|
||||||
|
directions=direction)
|
||||||
|
|
||||||
def route_vdd_gnd(self):
|
def route_vdd_gnd(self):
|
||||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||||
|
|
@ -230,14 +258,16 @@ class hierarchical_predecode(design.design):
|
||||||
for n in ["vdd", "gnd"]:
|
for n in ["vdd", "gnd"]:
|
||||||
and_pin = self.and_inst[num].get_pin(n)
|
and_pin = self.and_inst[num].get_pin(n)
|
||||||
supply_offset = and_pin.ll().scale(0, 1)
|
supply_offset = and_pin.ll().scale(0, 1)
|
||||||
self.add_rect(layer="m1",
|
self.add_rect(layer=and_pin.layer,
|
||||||
offset=supply_offset,
|
offset=supply_offset,
|
||||||
width=self.and_inst[num].rx())
|
width=self.and_inst[num].rx())
|
||||||
|
|
||||||
# Add pins in two locations
|
# Add pins in two locations
|
||||||
for xoffset in [in_xoffset]:
|
for xoffset in [in_xoffset]:
|
||||||
pin_pos = vector(xoffset, and_pin.cy())
|
pin_pos = vector(xoffset, and_pin.cy())
|
||||||
self.add_power_pin(n, pin_pos)
|
self.add_power_pin(name=n,
|
||||||
|
loc=pin_pos,
|
||||||
|
start_layer=and_pin.layer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California
|
# Copyright (c) 2016-2019 Regents of the University of California
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import sys
|
|
||||||
from tech import drc, parameter
|
|
||||||
from math import log, ceil
|
from math import log, ceil
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
|
|
@ -13,6 +11,7 @@ from vector import vector
|
||||||
|
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class port_address(design.design):
|
class port_address(design.design):
|
||||||
"""
|
"""
|
||||||
Create the address port (row decoder and wordline driver)..
|
Create the address port (row decoder and wordline driver)..
|
||||||
|
|
@ -25,17 +24,16 @@ class port_address(design.design):
|
||||||
self.addr_size = ceil(log(self.num_rows, 2))
|
self.addr_size = ceil(log(self.num_rows, 2))
|
||||||
|
|
||||||
if name == "":
|
if name == "":
|
||||||
name = "port_address_{0}_{1}".format(cols,rows)
|
name = "port_address_{0}_{1}".format(cols, rows)
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(2, "create data port of cols {0} rows {1}".format(cols,rows))
|
debug.info(2, "create data port of cols {0} rows {1}".format(cols, rows))
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
|
debug.check(len(self.all_ports) <= 2, "Bank layout cannot handle more than two ports.")
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
|
@ -51,16 +49,15 @@ class port_address(design.design):
|
||||||
""" Adding pins for port address module"""
|
""" Adding pins for port address module"""
|
||||||
|
|
||||||
for bit in range(self.addr_size):
|
for bit in range(self.addr_size):
|
||||||
self.add_pin("addr_{0}".format(bit),"INPUT")
|
self.add_pin("addr_{0}".format(bit), "INPUT")
|
||||||
|
|
||||||
self.add_pin("wl_en", "INPUT")
|
self.add_pin("wl_en", "INPUT")
|
||||||
|
|
||||||
for bit in range(self.num_rows):
|
for bit in range(self.num_rows):
|
||||||
self.add_pin("wl_{0}".format(bit),"OUTPUT")
|
self.add_pin("wl_{0}".format(bit), "OUTPUT")
|
||||||
|
|
||||||
self.add_pin("vdd","POWER")
|
|
||||||
self.add_pin("gnd","GROUND")
|
|
||||||
|
|
||||||
|
self.add_pin("vdd", "POWER")
|
||||||
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def route_layout(self):
|
def route_layout(self):
|
||||||
""" Create routing amoung the modules """
|
""" Create routing amoung the modules """
|
||||||
|
|
@ -71,8 +68,8 @@ class port_address(design.design):
|
||||||
def route_supplies(self):
|
def route_supplies(self):
|
||||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||||
for inst in self.insts:
|
for inst in self.insts:
|
||||||
self.copy_power_pins(inst,"vdd")
|
self.copy_power_pins(inst, "vdd")
|
||||||
self.copy_power_pins(inst,"gnd")
|
self.copy_power_pins(inst, "gnd")
|
||||||
|
|
||||||
def route_pins(self):
|
def route_pins(self):
|
||||||
for row in range(self.addr_size):
|
for row in range(self.addr_size):
|
||||||
|
|
@ -119,8 +116,6 @@ class port_address(design.design):
|
||||||
temp.extend(["vdd", "gnd"])
|
temp.extend(["vdd", "gnd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_wordline_driver(self):
|
def create_wordline_driver(self):
|
||||||
""" Create the Wordline Driver """
|
""" Create the Wordline Driver """
|
||||||
|
|
||||||
|
|
@ -137,19 +132,13 @@ class port_address(design.design):
|
||||||
temp.append("gnd")
|
temp.append("gnd")
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def place_instances(self):
|
def place_instances(self):
|
||||||
"""
|
"""
|
||||||
Compute the offsets and place the instances.
|
Compute the offsets and place the instances.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# A space for wells or jogging m2
|
row_decoder_offset = vector(0, 0)
|
||||||
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
wordline_driver_offset = vector(self.row_decoder.width, 0)
|
||||||
3*self.m2_pitch)
|
|
||||||
|
|
||||||
row_decoder_offset = vector(0,0)
|
|
||||||
wordline_driver_offset = vector(self.row_decoder.width + self.m2_gap,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)
|
||||||
|
|
|
||||||
|
|
@ -789,7 +789,7 @@ class port_data(design.design):
|
||||||
|
|
||||||
# Channel route each mux separately since we don't minimize the number
|
# Channel route each mux separately since we don't minimize the number
|
||||||
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
||||||
offset = bot_inst_group.inst.ul() + vector(0, self.m1_pitch)
|
offset = bot_inst_group.inst.ul() + vector(0, self.m1_nonpref_pitch)
|
||||||
for bit in range(num_bits):
|
for bit in range(num_bits):
|
||||||
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
|
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
|
||||||
top_names = self._get_bitline_pins(top_inst_group, bit)
|
top_names = self._get_bitline_pins(top_inst_group, bit)
|
||||||
|
|
|
||||||
|
|
@ -355,31 +355,29 @@ class replica_bitcell_array(design.design):
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=pin.height())
|
height=pin.height())
|
||||||
|
|
||||||
|
|
||||||
# Replica bitlines
|
# Replica bitlines
|
||||||
for port in range(self.left_rbl+self.right_rbl):
|
for port in range(self.left_rbl + self.right_rbl):
|
||||||
inst = self.replica_col_inst[port]
|
inst = self.replica_col_inst[port]
|
||||||
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(),self.replica_bl_names[port]):
|
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bl_names[port]):
|
||||||
pin = inst.get_pin(pin_name)
|
pin = inst.get_pin(pin_name)
|
||||||
if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names:
|
if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names:
|
||||||
name = bl_name
|
name = bl_name
|
||||||
else:
|
else:
|
||||||
name = "rbl_{0}_{1}".format(pin_name,port)
|
name = "rbl_{0}_{1}".format(pin_name, port)
|
||||||
self.add_layout_pin(text=name,
|
self.add_layout_pin(text=name,
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
offset=pin.ll().scale(1,0),
|
offset=pin.ll().scale(1, 0),
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
|
for pin_name in ["vdd", "gnd"]:
|
||||||
for pin_name in ["vdd","gnd"]:
|
|
||||||
for inst in self.insts:
|
for inst in self.insts:
|
||||||
pin_list = inst.get_pins(pin_name)
|
pin_list = inst.get_pins(pin_name)
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
|
self.add_power_pin(name=pin_name,
|
||||||
|
loc=pin.center(),
|
||||||
|
directions=("V", "V"),
|
||||||
|
start_layer=pin.layer)
|
||||||
|
|
||||||
def get_rbl_wl_name(self, port):
|
def get_rbl_wl_name(self, port):
|
||||||
""" Return the WL for the given RBL port """
|
""" Return the WL for the given RBL port """
|
||||||
|
|
|
||||||
|
|
@ -151,13 +151,13 @@ class sense_amp_array(design.design):
|
||||||
self.add_power_pin(name="gnd",
|
self.add_power_pin(name="gnd",
|
||||||
loc=gnd_pin.center(),
|
loc=gnd_pin.center(),
|
||||||
start_layer=gnd_pin.layer,
|
start_layer=gnd_pin.layer,
|
||||||
vertical=True)
|
directions=("V", "V"))
|
||||||
|
|
||||||
vdd_pin = inst.get_pin("vdd")
|
vdd_pin = inst.get_pin("vdd")
|
||||||
self.add_power_pin(name="vdd",
|
self.add_power_pin(name="vdd",
|
||||||
loc=vdd_pin.center(),
|
loc=vdd_pin.center(),
|
||||||
start_layer=vdd_pin.layer,
|
start_layer=vdd_pin.layer,
|
||||||
vertical=True)
|
directions=("V", "V"))
|
||||||
|
|
||||||
bl_pin = inst.get_pin(inst.mod.get_bl_names())
|
bl_pin = inst.get_pin(inst.mod.get_bl_names())
|
||||||
br_pin = inst.get_pin(inst.mod.get_br_names())
|
br_pin = inst.get_pin(inst.mod.get_br_names())
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
import math
|
import math
|
||||||
import contact
|
from tech import drc
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
@ -26,8 +26,8 @@ class wordline_driver(design.design):
|
||||||
debug.info(1, "Creating {0}".format(self.name))
|
debug.info(1, "Creating {0}".format(self.name))
|
||||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||||
|
|
||||||
self.rows = rows
|
self.bitcell_rows = rows
|
||||||
self.cols = cols
|
self.bitcell_cols = cols
|
||||||
|
|
||||||
b = factory.create(module_type="bitcell")
|
b = factory.create(module_type="bitcell")
|
||||||
try:
|
try:
|
||||||
|
|
@ -36,6 +36,11 @@ class wordline_driver(design.design):
|
||||||
self.cell_multiple = 1
|
self.cell_multiple = 1
|
||||||
self.cell_height = self.cell_multiple * b.height
|
self.cell_height = self.cell_multiple * b.height
|
||||||
|
|
||||||
|
# We may have more than one bitcell per decoder row
|
||||||
|
self.num_rows = math.ceil(self.bitcell_rows / self.cell_multiple)
|
||||||
|
# We will place this many final decoders per row
|
||||||
|
self.decoders_per_row = math.ceil(self.bitcell_rows / self.num_rows)
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -56,10 +61,10 @@ class wordline_driver(design.design):
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
# inputs to wordline_driver.
|
# inputs to wordline_driver.
|
||||||
for i in range(self.rows):
|
for i in range(self.bitcell_rows):
|
||||||
self.add_pin("in_{0}".format(i), "INPUT")
|
self.add_pin("in_{0}".format(i), "INPUT")
|
||||||
# Outputs from wordline_driver.
|
# Outputs from wordline_driver.
|
||||||
for i in range(self.rows):
|
for i in range(self.bitcell_rows):
|
||||||
self.add_pin("wl_{0}".format(i), "OUTPUT")
|
self.add_pin("wl_{0}".format(i), "OUTPUT")
|
||||||
self.add_pin("en", "INPUT")
|
self.add_pin("en", "INPUT")
|
||||||
self.add_pin("vdd", "POWER")
|
self.add_pin("vdd", "POWER")
|
||||||
|
|
@ -68,7 +73,7 @@ class wordline_driver(design.design):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.and2 = factory.create(module_type="pand2",
|
self.and2 = factory.create(module_type="pand2",
|
||||||
height=self.cell_height,
|
height=self.cell_height,
|
||||||
size=self.cols)
|
size=self.bitcell_cols)
|
||||||
self.add_mod(self.and2)
|
self.add_mod(self.and2)
|
||||||
|
|
||||||
def route_vdd_gnd(self):
|
def route_vdd_gnd(self):
|
||||||
|
|
@ -79,7 +84,7 @@ class wordline_driver(design.design):
|
||||||
|
|
||||||
# Find the x offsets for where the vias/pins should be placed
|
# Find the x offsets for where the vias/pins should be placed
|
||||||
xoffset_list = [self.and_inst[0].lx()]
|
xoffset_list = [self.and_inst[0].lx()]
|
||||||
for num in range(self.rows):
|
for num in range(self.bitcell_rows):
|
||||||
# this will result in duplicate polygons for rails, but who cares
|
# this will result in duplicate polygons for rails, but who cares
|
||||||
|
|
||||||
# use the inverter offset even though it will be the and's too
|
# use the inverter offset even though it will be the and's too
|
||||||
|
|
@ -97,32 +102,34 @@ class wordline_driver(design.design):
|
||||||
|
|
||||||
def create_drivers(self):
|
def create_drivers(self):
|
||||||
self.and_inst = []
|
self.and_inst = []
|
||||||
for row in range(self.rows):
|
for row in range(self.bitcell_rows):
|
||||||
name_and = "wl_driver_and{}".format(row)
|
name_and = "wl_driver_and{}".format(row)
|
||||||
|
|
||||||
# add and2
|
# add and2
|
||||||
self.and_inst.append(self.add_inst(name=name_and,
|
self.and_inst.append(self.add_inst(name=name_and,
|
||||||
mod=self.and2))
|
mod=self.and2))
|
||||||
self.connect_inst(["en",
|
self.connect_inst(["in_{0}".format(row),
|
||||||
"in_{0}".format(row),
|
"en",
|
||||||
"wl_{0}".format(row),
|
"wl_{0}".format(row),
|
||||||
"vdd", "gnd"])
|
"vdd", "gnd"])
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
# We may have more than one bitcell per decoder row
|
# We may have more than one bitcell per decoder row
|
||||||
self.num_rows = math.ceil(self.rows / self.cell_multiple)
|
self.driver_rows = math.ceil(self.bitcell_rows / self.cell_multiple)
|
||||||
# We will place this many final decoders per row
|
# We will place this many final decoders per row
|
||||||
self.decoders_per_row = math.ceil(self.rows / self.num_rows)
|
self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows)
|
||||||
|
|
||||||
def place_drivers(self):
|
def place_drivers(self):
|
||||||
and2_xoffset = 2 * self.m1_width + 5 * self.m1_space
|
|
||||||
|
|
||||||
self.width = and2_xoffset + self.and2.width
|
# Leave a well gap to separate the bitcell array well from this well
|
||||||
self.height = self.and2.height * self.num_rows
|
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
|
||||||
|
|
||||||
for row in range(self.rows):
|
self.width = self.decoders_per_row * self.and2.width + well_gap
|
||||||
#row = math.floor(inst_index / self.decoders_per_row)
|
self.height = self.and2.height * self.driver_rows
|
||||||
#dec = inst_index % self.decoders_per_row
|
|
||||||
|
for inst_index in range(self.bitcell_rows):
|
||||||
|
row = math.floor(inst_index / self.decoders_per_row)
|
||||||
|
dec = inst_index % self.decoders_per_row
|
||||||
|
|
||||||
if (row % 2):
|
if (row % 2):
|
||||||
y_offset = self.and2.height * (row + 1)
|
y_offset = self.and2.height * (row + 1)
|
||||||
|
|
@ -131,63 +138,42 @@ class wordline_driver(design.design):
|
||||||
y_offset = self.and2.height * row
|
y_offset = self.and2.height * row
|
||||||
inst_mirror = "R0"
|
inst_mirror = "R0"
|
||||||
|
|
||||||
# x_off = self.internal_routing_width + dec * and_mod.width
|
x_offset = dec * self.and2.width
|
||||||
and2_offset = [and2_xoffset, y_offset]
|
and2_offset = [x_offset, y_offset]
|
||||||
|
|
||||||
# add and2
|
# add and2
|
||||||
self.and_inst[row].place(offset=and2_offset,
|
self.and_inst[inst_index].place(offset=and2_offset,
|
||||||
mirror=inst_mirror)
|
mirror=inst_mirror)
|
||||||
|
|
||||||
def route_layout(self):
|
def route_layout(self):
|
||||||
""" Route all of the signals """
|
""" Route all of the signals """
|
||||||
|
|
||||||
# Wordline enable connection
|
# Wordline enable connection
|
||||||
en_offset = [self.m1_width + 2 * self.m1_space, 0]
|
en_pin = self.and_inst[0].get_pin("B")
|
||||||
|
en_bottom_pos = vector(en_pin.lx(), 0)
|
||||||
en_pin = self.add_layout_pin(text="en",
|
en_pin = self.add_layout_pin(text="en",
|
||||||
layer="m2",
|
layer="m2",
|
||||||
offset=en_offset,
|
offset=en_bottom_pos,
|
||||||
width=self.m2_width,
|
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
for row in range(self.rows):
|
for inst_index in range(self.bitcell_rows):
|
||||||
and_inst = self.and_inst[row]
|
and_inst = self.and_inst[inst_index]
|
||||||
|
row = math.floor(inst_index / self.decoders_per_row)
|
||||||
|
|
||||||
# en connection
|
# Drop a via
|
||||||
a_pin = and_inst.get_pin("A")
|
|
||||||
a_pos = a_pin.lc()
|
|
||||||
clk_offset = vector(en_pin.bc().x, a_pos.y)
|
|
||||||
self.add_segment_center(layer="m1",
|
|
||||||
start=clk_offset,
|
|
||||||
end=a_pos)
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=clk_offset)
|
|
||||||
|
|
||||||
# connect the decoder input pin to and2 B
|
|
||||||
b_pin = and_inst.get_pin("B")
|
b_pin = and_inst.get_pin("B")
|
||||||
b_pos = b_pin.lc()
|
self.add_via_stack_center(from_layer=b_pin.layer,
|
||||||
# needs to move down since B and input is
|
to_layer="m2",
|
||||||
# nearly aligned with A inv input
|
offset=b_pin.center())
|
||||||
up_or_down = self.m2_space if row % 2 else -self.m2_space
|
|
||||||
input_offset = vector(0, b_pos.y + up_or_down)
|
|
||||||
base_offset = vector(clk_offset.x, input_offset.y)
|
|
||||||
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
|
|
||||||
mid_via_offset = base_offset + contact_offset
|
|
||||||
|
|
||||||
|
# connect the decoder input pin to and2 A
|
||||||
|
a_pin = and_inst.get_pin("A")
|
||||||
|
a_pos = a_pin.center()
|
||||||
# must under the clk line in M1
|
# must under the clk line in M1
|
||||||
self.add_layout_pin_segment_center(text="in_{0}".format(row),
|
self.add_layout_pin_segment_center(text="in_{0}".format(row),
|
||||||
layer="m1",
|
layer="m1",
|
||||||
start=input_offset,
|
start=vector(0, a_pos.y),
|
||||||
end=mid_via_offset)
|
end=a_pos)
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=mid_via_offset,
|
|
||||||
directions=("V", "V"))
|
|
||||||
|
|
||||||
# now connect to the and2 B
|
|
||||||
self.add_path("m2", [mid_via_offset, b_pos])
|
|
||||||
contact_offset = b_pos - vector(0.5 * contact.m1_via.height, 0)
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=contact_offset,
|
|
||||||
directions=("H", "H"))
|
|
||||||
|
|
||||||
# output each WL on the right
|
# output each WL on the right
|
||||||
wl_offset = and_inst.get_pin("Z").rc()
|
wl_offset = and_inst.get_pin("Z").rc()
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ class write_driver_array(design.design):
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
self.add_power_pin(name=n,
|
self.add_power_pin(name=n,
|
||||||
loc=pin.center(),
|
loc=pin.center(),
|
||||||
vertical=True,
|
directions=("V", "V"),
|
||||||
start_layer=pin.layer)
|
start_layer=pin.layer)
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
|
|
|
||||||
|
|
@ -5,9 +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.
|
||||||
#
|
#
|
||||||
from math import log
|
|
||||||
import design
|
import design
|
||||||
from tech import drc
|
|
||||||
import debug
|
import debug
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
|
|
@ -43,33 +41,28 @@ class write_mask_and_array(design.design):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_and2_array()
|
self.create_and2_array()
|
||||||
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.place_and2_array()
|
self.place_and2_array()
|
||||||
spacing = self.wmask_en_len - self.and2.width
|
|
||||||
self.width = (self.num_wmasks*self.and2.width) + ((self.num_wmasks-1)*spacing)
|
|
||||||
self.height = self.and2.height
|
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
self.add_pin("wmask_in_{}".format(bit),"INPUT")
|
self.add_pin("wmask_in_{}".format(bit), "INPUT")
|
||||||
self.add_pin("en", "INPUT")
|
self.add_pin("en", "INPUT")
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
self.add_pin("wmask_out_{}".format(bit),"OUTPUT")
|
self.add_pin("wmask_out_{}".format(bit), "OUTPUT")
|
||||||
self.add_pin("vdd","POWER")
|
self.add_pin("vdd", "POWER")
|
||||||
self.add_pin("gnd","GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
# Size the AND gate for the number of write drivers it drives, which is equal to the write size.
|
# Size the AND gate for the number of write drivers it drives, which is equal to the write size.
|
||||||
# Assume stage effort of 3 to compute the size
|
# Assume stage effort of 3 to compute the size
|
||||||
self.and2 = factory.create(module_type="pand2",
|
self.and2 = factory.create(module_type="pand2",
|
||||||
size=self.write_size/4.0)
|
size=self.write_size / 4.0)
|
||||||
self.add_mod(self.and2)
|
self.add_mod(self.and2)
|
||||||
|
|
||||||
|
|
||||||
def create_and2_array(self):
|
def create_and2_array(self):
|
||||||
self.and2_insts = {}
|
self.and2_insts = {}
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
|
|
@ -81,7 +74,6 @@ class write_mask_and_array(design.design):
|
||||||
"wmask_out_{}".format(bit),
|
"wmask_out_{}".format(bit),
|
||||||
"vdd", "gnd"])
|
"vdd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
def place_and2_array(self):
|
def place_and2_array(self):
|
||||||
# Place the write mask AND array at the start of each write driver enable length.
|
# Place the write mask AND array at the start of each write driver enable length.
|
||||||
# This ensures the write mask AND array will be directly under the corresponding write driver enable wire.
|
# This ensures the write mask AND array will be directly under the corresponding write driver enable wire.
|
||||||
|
|
@ -96,56 +88,45 @@ class write_mask_and_array(design.design):
|
||||||
|
|
||||||
self.wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
|
self.wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
|
||||||
debug.check(self.wmask_en_len >= self.and2.width,
|
debug.check(self.wmask_en_len >= self.and2.width,
|
||||||
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,self.wmask_en_len))
|
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,
|
||||||
|
self.wmask_en_len))
|
||||||
|
|
||||||
|
self.width = self.bitcell.width * self.columns
|
||||||
|
self.height = self.and2.height
|
||||||
|
|
||||||
for i in range(self.num_wmasks):
|
for i in range(self.num_wmasks):
|
||||||
base = vector(i * self.wmask_en_len, 0)
|
base = vector(i * self.wmask_en_len, 0)
|
||||||
self.and2_insts[i].place(base)
|
self.and2_insts[i].place(base)
|
||||||
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
# Create the enable pin that connects all write mask AND array's B pins
|
# Create the enable pin that connects all write mask AND array's B pins
|
||||||
beg_en_pin = self.and2_insts[0].get_pin("B")
|
en_pin = self.and2_insts[0].get_pin("B")
|
||||||
end_en_pin = self.and2_insts[self.num_wmasks-1].get_pin("B")
|
self.add_layout_pin_segment_center(text="en",
|
||||||
if self.port % 2:
|
layer="m3",
|
||||||
# Extend metal3 to edge of AND array in multiport
|
start=vector(0, en_pin.cy()),
|
||||||
en_to_edge = self.and2.width - beg_en_pin.cx()
|
end=vector(self.width, en_pin.cy()))
|
||||||
self.add_layout_pin(text="en",
|
|
||||||
layer="m3",
|
|
||||||
offset=beg_en_pin.bc(),
|
|
||||||
width=end_en_pin.cx() - beg_en_pin.cx() + en_to_edge)
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
|
|
||||||
self.add_via_center(layers=self.m2_stack,
|
|
||||||
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
|
|
||||||
else:
|
|
||||||
self.add_layout_pin(text="en",
|
|
||||||
layer="m3",
|
|
||||||
offset=beg_en_pin.bc(),
|
|
||||||
width=end_en_pin.cx() - beg_en_pin.cx())
|
|
||||||
|
|
||||||
for i in range(self.num_wmasks):
|
for i in range(self.num_wmasks):
|
||||||
# Copy remaining layout pins
|
# Copy remaining layout pins
|
||||||
self.copy_layout_pin(self.and2_insts[i],"A","wmask_in_{0}".format(i))
|
self.copy_layout_pin(self.and2_insts[i], "A", "wmask_in_{0}".format(i))
|
||||||
self.copy_layout_pin(self.and2_insts[i],"Z","wmask_out_{0}".format(i))
|
self.copy_layout_pin(self.and2_insts[i], "Z", "wmask_out_{0}".format(i))
|
||||||
|
|
||||||
# Add via connections to metal3 for AND array's B pin
|
# Add via connections to metal3 for AND array's B pin
|
||||||
en_pin = self.and2_insts[i].get_pin("B")
|
en_pin = self.and2_insts[i].get_pin("B")
|
||||||
self.add_via_center(layers=self.m1_stack,
|
en_pos = en_pin.center()
|
||||||
offset=en_pin.center())
|
self.add_via_stack_center(from_layer=en_pin.layer,
|
||||||
self.add_via_center(layers=self.m2_stack,
|
to_layer="m3",
|
||||||
offset=en_pin.center())
|
offset=en_pos)
|
||||||
|
|
||||||
for supply in ["gnd", "vdd"]:
|
for supply in ["gnd", "vdd"]:
|
||||||
supply_pin=self.and2_insts[i].get_pin(supply)
|
supply_pin=self.and2_insts[i].get_pin(supply)
|
||||||
self.add_power_pin(supply, supply_pin.center())
|
self.add_power_pin(supply, supply_pin.center())
|
||||||
|
|
||||||
|
|
||||||
for supply in ["gnd", "vdd"]:
|
for supply in ["gnd", "vdd"]:
|
||||||
supply_pin_left = self.and2_insts[0].get_pin(supply)
|
supply_pin_left = self.and2_insts[0].get_pin(supply)
|
||||||
supply_pin_right = self.and2_insts[self.num_wmasks-1].get_pin(supply)
|
supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply)
|
||||||
self.add_path("m1",[supply_pin_left.lc(), supply_pin_right.rc()])
|
self.add_path("m1", [supply_pin_left.lc(), supply_pin_right.rc()])
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
"""Get the relative capacitance of all the input connections in the bank"""
|
"""Get the relative capacitance of all the input connections in the bank"""
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ class pand2(pgate.pgate):
|
||||||
self.place_insts()
|
self.place_insts()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
self.route_supply_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
|
@ -76,26 +77,10 @@ class pand2(pgate.pgate):
|
||||||
a2_pin = self.inv_inst.get_pin("A")
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
|
mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
|
||||||
mid2_point = vector(mid1_point, a2_pin.cy())
|
mid2_point = vector(mid1_point, a2_pin.cy())
|
||||||
self.add_path("m1",
|
self.add_path(self.route_layer,
|
||||||
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
|
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
# Continous vdd rail along with label.
|
|
||||||
vdd_pin = self.inv_inst.get_pin("vdd")
|
|
||||||
self.add_layout_pin(text="vdd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vdd_pin.ll().scale(0, 1),
|
|
||||||
width=self.width,
|
|
||||||
height=vdd_pin.height())
|
|
||||||
|
|
||||||
# Continous gnd rail along with label.
|
|
||||||
gnd_pin = self.inv_inst.get_pin("gnd")
|
|
||||||
self.add_layout_pin(text="gnd",
|
|
||||||
layer="m1",
|
|
||||||
offset=gnd_pin.ll().scale(0, 1),
|
|
||||||
width=self.width,
|
|
||||||
height=vdd_pin.height())
|
|
||||||
|
|
||||||
pin = self.inv_inst.get_pin("Z")
|
pin = self.inv_inst.get_pin("Z")
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ class pand3(pgate.pgate):
|
||||||
self.place_insts()
|
self.place_insts()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
self.route_supply_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
|
@ -77,26 +78,10 @@ class pand3(pgate.pgate):
|
||||||
a2_pin = self.inv_inst.get_pin("A")
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
|
mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
|
||||||
mid2_point = vector(mid1_point, a2_pin.cy())
|
mid2_point = vector(mid1_point, a2_pin.cy())
|
||||||
self.add_path("m1",
|
self.add_path(z1_pin.layer,
|
||||||
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
|
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
# Continous vdd rail along with label.
|
|
||||||
vdd_pin = self.inv_inst.get_pin("vdd")
|
|
||||||
self.add_layout_pin(text="vdd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vdd_pin.ll().scale(0, 1),
|
|
||||||
width=self.width,
|
|
||||||
height=vdd_pin.height())
|
|
||||||
|
|
||||||
# Continous gnd rail along with label.
|
|
||||||
gnd_pin = self.inv_inst.get_pin("gnd")
|
|
||||||
self.add_layout_pin(text="gnd",
|
|
||||||
layer="m1",
|
|
||||||
offset=gnd_pin.ll().scale(0, 1),
|
|
||||||
width=self.width,
|
|
||||||
height=vdd_pin.height())
|
|
||||||
|
|
||||||
pin = self.inv_inst.get_pin("Z")
|
pin = self.inv_inst.get_pin("Z")
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ class pbuf(pgate.pgate):
|
||||||
self.place_insts()
|
self.place_insts()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
self.route_supply_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
@ -78,26 +79,9 @@ class pbuf(pgate.pgate):
|
||||||
# inv1 Z to inv2 A
|
# inv1 Z to inv2 A
|
||||||
z1_pin = self.inv1_inst.get_pin("Z")
|
z1_pin = self.inv1_inst.get_pin("Z")
|
||||||
a2_pin = self.inv2_inst.get_pin("A")
|
a2_pin = self.inv2_inst.get_pin("A")
|
||||||
mid_point = vector(z1_pin.cx(), a2_pin.cy())
|
self.add_zjog(self.route_layer, z1_pin.center(), a2_pin.center())
|
||||||
self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()])
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
# Continous vdd rail along with label.
|
|
||||||
vdd_pin = self.inv1_inst.get_pin("vdd")
|
|
||||||
self.add_layout_pin(text="vdd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vdd_pin.ll().scale(0, 1),
|
|
||||||
width=self.width,
|
|
||||||
height=vdd_pin.height())
|
|
||||||
|
|
||||||
# Continous gnd rail along with label.
|
|
||||||
gnd_pin = self.inv1_inst.get_pin("gnd")
|
|
||||||
self.add_layout_pin(text="gnd",
|
|
||||||
layer="m1",
|
|
||||||
offset=gnd_pin.ll().scale(0, 1),
|
|
||||||
width=self.width,
|
|
||||||
height=vdd_pin.height())
|
|
||||||
|
|
||||||
z_pin = self.inv2_inst.get_pin("Z")
|
z_pin = self.inv2_inst.get_pin("Z")
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
layer=z_pin.layer,
|
layer=z_pin.layer,
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ class pdriver(pgate.pgate):
|
||||||
|
|
||||||
self.width = self.inv_inst_list[-1].rx()
|
self.width = self.inv_inst_list[-1].rx()
|
||||||
self.height = self.inv_inst_list[0].height
|
self.height = self.inv_inst_list[0].height
|
||||||
|
self.route_supply_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
@ -141,26 +142,11 @@ class pdriver(pgate.pgate):
|
||||||
z_inst_list.append(self.inv_inst_list[x].get_pin("Z"))
|
z_inst_list.append(self.inv_inst_list[x].get_pin("Z"))
|
||||||
a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A"))
|
a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A"))
|
||||||
mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy())
|
mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy())
|
||||||
self.add_path("m1",
|
self.add_path(self.route_layer,
|
||||||
[z_inst_list[x].center(), mid_point,
|
[z_inst_list[x].center(), mid_point,
|
||||||
a_inst_list[x].center()])
|
a_inst_list[x].center()])
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
# Continous vdd rail along with label.
|
|
||||||
vdd_pin = self.inv_inst_list[0].get_pin("vdd")
|
|
||||||
self.add_layout_pin(text="vdd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vdd_pin.ll().scale(0, 1),
|
|
||||||
width=self.width,
|
|
||||||
height=vdd_pin.height())
|
|
||||||
|
|
||||||
# Continous gnd rail along with label.
|
|
||||||
gnd_pin = self.inv_inst_list[0].get_pin("gnd")
|
|
||||||
self.add_layout_pin(text="gnd",
|
|
||||||
layer="m1",
|
|
||||||
offset=gnd_pin.ll().scale(0, 1),
|
|
||||||
width=self.width,
|
|
||||||
height=vdd_pin.height())
|
|
||||||
|
|
||||||
z_pin = self.inv_inst_list[len(self.inv_inst_list) - 1].get_pin("Z")
|
z_pin = self.inv_inst_list[len(self.inv_inst_list) - 1].get_pin("Z")
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ from globals import OPTS
|
||||||
if(OPTS.tech_name == "s8"):
|
if(OPTS.tech_name == "s8"):
|
||||||
from tech import nmos_bins, pmos_bins, accuracy_requirement
|
from tech import nmos_bins, pmos_bins, accuracy_requirement
|
||||||
|
|
||||||
|
|
||||||
class pgate(design.design):
|
class pgate(design.design):
|
||||||
"""
|
"""
|
||||||
This is a module that implements some shared
|
This is a module that implements some shared
|
||||||
|
|
@ -30,8 +31,24 @@ class pgate(design.design):
|
||||||
if height:
|
if height:
|
||||||
self.height = height
|
self.height = height
|
||||||
elif not height:
|
elif not height:
|
||||||
# By default, we make it 10 M1 pitch tall
|
# By default, something simple
|
||||||
self.height = 10*self.m1_pitch
|
self.height = 14 * self.m1_pitch
|
||||||
|
|
||||||
|
if "li" in layer:
|
||||||
|
self.route_layer = "li"
|
||||||
|
else:
|
||||||
|
self.route_layer = "m1"
|
||||||
|
self.route_layer_width = getattr(self, "{}_width".format(self.route_layer))
|
||||||
|
self.route_layer_space = getattr(self, "{}_space".format(self.route_layer))
|
||||||
|
self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer))
|
||||||
|
|
||||||
|
# This is the space from a S/D contact to the supply rail
|
||||||
|
# Assume the contact starts at the active edge
|
||||||
|
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space
|
||||||
|
# This is a poly-to-poly of a flipped cell
|
||||||
|
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
|
||||||
|
self.top_bottom_space = max(contact_to_vdd_rail_space,
|
||||||
|
poly_to_poly_gate_space)
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
|
|
@ -47,27 +64,27 @@ class pgate(design.design):
|
||||||
""" Pure virtual function """
|
""" Pure virtual function """
|
||||||
debug.error("Must over-ride create_layout.", -1)
|
debug.error("Must over-ride create_layout.", -1)
|
||||||
|
|
||||||
def connect_pin_to_rail(self, inst, pin, supply):
|
def connect_pin_to_rail(self, inst, pin_name, supply_name):
|
||||||
""" Connects a ptx pin to a supply rail. """
|
""" Connects a ptx pin to a supply rail. """
|
||||||
source_pin = inst.get_pin(pin)
|
supply_pin = self.get_pin(supply_name)
|
||||||
supply_pin = self.get_pin(supply)
|
|
||||||
if supply_pin.overlaps(source_pin):
|
|
||||||
return
|
|
||||||
|
|
||||||
if supply == "gnd":
|
source_pins = inst.get_pins(pin_name)
|
||||||
height = supply_pin.by() - source_pin.by()
|
for source_pin in source_pins:
|
||||||
elif supply == "vdd":
|
|
||||||
height = supply_pin.uy() - source_pin.by()
|
|
||||||
else:
|
|
||||||
debug.error("Invalid supply name.", -1)
|
|
||||||
|
|
||||||
if abs(height) > 0:
|
if supply_name == "gnd":
|
||||||
self.add_rect(layer="m1",
|
height = supply_pin.by() - source_pin.by()
|
||||||
|
elif supply_name == "vdd":
|
||||||
|
height = supply_pin.uy() - source_pin.by()
|
||||||
|
else:
|
||||||
|
debug.error("Invalid supply name.", -1)
|
||||||
|
|
||||||
|
debug.check(supply_pin.layer == source_pin.layer, "Supply pin is not on correct layer.")
|
||||||
|
self.add_rect(layer=source_pin.layer,
|
||||||
offset=source_pin.ll(),
|
offset=source_pin.ll(),
|
||||||
height=height,
|
height=height,
|
||||||
width=source_pin.width())
|
width=source_pin.width())
|
||||||
|
|
||||||
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"):
|
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None):
|
||||||
"""
|
"""
|
||||||
Route the input gate to the left side of the cell for access.
|
Route the input gate to the left side of the cell for access.
|
||||||
Position specifies to place the contact the left, center, or
|
Position specifies to place the contact the left, center, or
|
||||||
|
|
@ -93,8 +110,6 @@ class pgate(design.design):
|
||||||
|
|
||||||
# Center is completely symmetric.
|
# Center is completely symmetric.
|
||||||
contact_width = contact.poly_contact.width
|
contact_width = contact.poly_contact.width
|
||||||
contact_m1_width = contact.poly_contact.second_layer_width
|
|
||||||
contact_m1_height = contact.poly_contact.second_layer_height
|
|
||||||
|
|
||||||
if position == "center":
|
if position == "center":
|
||||||
contact_offset = left_gate_offset \
|
contact_offset = left_gate_offset \
|
||||||
|
|
@ -111,18 +126,16 @@ class pgate(design.design):
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid contact placement option.", -1)
|
debug.error("Invalid contact placement option.", -1)
|
||||||
|
|
||||||
if hasattr(self, "li_stack"):
|
via = self.add_via_stack_center(from_layer="poly",
|
||||||
self.add_via_center(layers=self.li_stack,
|
to_layer=self.route_layer,
|
||||||
offset=contact_offset)
|
offset=contact_offset,
|
||||||
|
directions=directions)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
|
||||||
offset=contact_offset)
|
|
||||||
|
|
||||||
self.add_layout_pin_rect_center(text=name,
|
self.add_layout_pin_rect_center(text=name,
|
||||||
layer="m1",
|
layer=self.route_layer,
|
||||||
offset=contact_offset,
|
offset=contact_offset,
|
||||||
width=contact_m1_width,
|
width=via.mod.second_layer_width,
|
||||||
height=contact_m1_height)
|
height=via.mod.second_layer_height)
|
||||||
# This is to ensure that the contact is
|
# This is to ensure that the contact is
|
||||||
# connected to the gate
|
# connected to the gate
|
||||||
mid_point = contact_offset.scale(0.5, 1) \
|
mid_point = contact_offset.scale(0.5, 1) \
|
||||||
|
|
@ -194,12 +207,10 @@ class pgate(design.design):
|
||||||
self.nwell_contact = self.add_via_center(layers=layer_stack,
|
self.nwell_contact = self.add_via_center(layers=layer_stack,
|
||||||
offset=contact_offset,
|
offset=contact_offset,
|
||||||
implant_type="n",
|
implant_type="n",
|
||||||
well_type="n")
|
well_type="n",
|
||||||
if hasattr(self, "li_stack"):
|
directions=("V", "V"))
|
||||||
self.add_via_center(layers=self.li_stack,
|
|
||||||
offset=contact_offset)
|
|
||||||
|
|
||||||
self.add_rect_center(layer="m1",
|
self.add_rect_center(layer=self.route_layer,
|
||||||
offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)),
|
offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)),
|
||||||
width=self.nwell_contact.mod.second_layer_width,
|
width=self.nwell_contact.mod.second_layer_width,
|
||||||
height=self.height - contact_offset.y)
|
height=self.height - contact_offset.y)
|
||||||
|
|
@ -249,13 +260,10 @@ class pgate(design.design):
|
||||||
self.pwell_contact= self.add_via_center(layers=layer_stack,
|
self.pwell_contact= self.add_via_center(layers=layer_stack,
|
||||||
offset=contact_offset,
|
offset=contact_offset,
|
||||||
implant_type="p",
|
implant_type="p",
|
||||||
well_type="p")
|
well_type="p",
|
||||||
|
directions=("V", "V"))
|
||||||
|
|
||||||
if hasattr(self, "li_stack"):
|
self.add_rect_center(layer=self.route_layer,
|
||||||
self.add_via_center(layers=self.li_stack,
|
|
||||||
offset=contact_offset)
|
|
||||||
|
|
||||||
self.add_rect_center(layer="m1",
|
|
||||||
offset=contact_offset.scale(1, 0.5),
|
offset=contact_offset.scale(1, 0.5),
|
||||||
width=self.pwell_contact.mod.second_layer_width,
|
width=self.pwell_contact.mod.second_layer_width,
|
||||||
height=contact_offset.y)
|
height=contact_offset.y)
|
||||||
|
|
@ -280,6 +288,18 @@ class pgate(design.design):
|
||||||
# width=implant_width,
|
# width=implant_width,
|
||||||
# height=implant_height)
|
# height=implant_height)
|
||||||
|
|
||||||
|
def route_supply_rails(self):
|
||||||
|
""" Add vdd/gnd rails to the top and bottom. """
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, 0),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, self.height),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
def determine_width(self):
|
def determine_width(self):
|
||||||
""" Determine the width based on the well contacts (assumed to be on the right side) """
|
""" Determine the width based on the well contacts (assumed to be on the right side) """
|
||||||
# Width is determined by well contact and spacing and allowing a supply via between each cell
|
# Width is determined by well contact and spacing and allowing a supply via between each cell
|
||||||
|
|
@ -287,7 +307,8 @@ class pgate(design.design):
|
||||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||||
# Height is an input parameter, so it is not recomputed.
|
# Height is an input parameter, so it is not recomputed.
|
||||||
|
|
||||||
def bin_width(self, tx_type, target_width):
|
@staticmethod
|
||||||
|
def bin_width(tx_type, target_width):
|
||||||
|
|
||||||
if tx_type == "nmos":
|
if tx_type == "nmos":
|
||||||
bins = nmos_bins[drc("minwidth_poly")]
|
bins = nmos_bins[drc("minwidth_poly")]
|
||||||
|
|
@ -303,22 +324,24 @@ class pgate(design.design):
|
||||||
scaled_bin = bins[0] * scaling_factor
|
scaled_bin = bins[0] * scaling_factor
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
base_bins = []
|
||||||
scaled_bins = []
|
scaled_bins = []
|
||||||
scaling_factors = []
|
scaling_factors = []
|
||||||
scaled_bins.append(bins[-1])
|
scaled_bins.append(bins[-1])
|
||||||
|
base_bins.append(bins[-1])
|
||||||
scaling_factors.append(1)
|
scaling_factors.append(1)
|
||||||
for width in bins[0:-1]:
|
for width in bins[0:-1]:
|
||||||
m = math.ceil(target_width / width)
|
m = math.ceil(target_width / width)
|
||||||
|
base_bins.append(width)
|
||||||
scaling_factors.append(m)
|
scaling_factors.append(m)
|
||||||
scaled_bins.append(m * width)
|
scaled_bins.append(m * width)
|
||||||
|
|
||||||
select = bisect_left(scaled_bins, target_width)
|
select = bisect_left(scaled_bins, target_width)
|
||||||
scaling_factor = scaling_factors[select]
|
scaling_factor = scaling_factors[select]
|
||||||
scaled_bin = scaled_bins[select]
|
scaled_bin = scaled_bins[select]
|
||||||
select = (select + 1) % len(scaled_bins)
|
selected_bin = base_bins[select]
|
||||||
selected_bin = bins[select]
|
|
||||||
|
|
||||||
debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, selected_bin, scaling_factor, scaled_bin, target_width))
|
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)
|
return(selected_bin, scaling_factor)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,17 @@ from errors import drc_error
|
||||||
if(OPTS.tech_name == "s8"):
|
if(OPTS.tech_name == "s8"):
|
||||||
from tech import nmos_bins, pmos_bins, accuracy_requirement
|
from tech import nmos_bins, pmos_bins, accuracy_requirement
|
||||||
|
|
||||||
|
|
||||||
class pinv(pgate.pgate):
|
class pinv(pgate.pgate):
|
||||||
"""
|
"""
|
||||||
Pinv generates gds of a parametrically sized inverter. The
|
Pinv generates gds of a parametrically sized inverter. The
|
||||||
size is specified as the drive size (relative to minimum NMOS) and
|
size is specified as the drive size (relative to minimum NMOS) and
|
||||||
a beta value for choosing the pmos size. The inverter's cell
|
a beta value for choosing the pmos size. The inverter's cell
|
||||||
height is usually the same as the 6t library cell and is measured
|
height is usually the same as the 6t library cell and is measured
|
||||||
from center of rail to rail.. The route_output will route the
|
from center of rail to rail.
|
||||||
output to the right side of the cell for easier access.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True):
|
def __init__(self, name, size=1, beta=parameter["beta"], height=None):
|
||||||
|
|
||||||
debug.info(2,
|
debug.info(2,
|
||||||
"creating pinv structure {0} with size of {1}".format(name,
|
"creating pinv structure {0} with size of {1}".format(name,
|
||||||
|
|
@ -43,7 +43,6 @@ class pinv(pgate.pgate):
|
||||||
self.nmos_size = size
|
self.nmos_size = size
|
||||||
self.pmos_size = beta * size
|
self.pmos_size = beta * size
|
||||||
self.beta = beta
|
self.beta = beta
|
||||||
self.route_output = False
|
|
||||||
|
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height)
|
||||||
|
|
||||||
|
|
@ -108,13 +107,6 @@ class pinv(pgate.pgate):
|
||||||
min_channel = max(contact.poly_contact.width + self.m1_space,
|
min_channel = max(contact.poly_contact.width + self.m1_space,
|
||||||
contact.poly_contact.width + 2 * self.poly_to_active)
|
contact.poly_contact.width + 2 * self.poly_to_active)
|
||||||
|
|
||||||
# This is the extra space needed to ensure DRC rules
|
|
||||||
# to the active contacts
|
|
||||||
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
|
|
||||||
# This is a poly-to-poly of a flipped cell
|
|
||||||
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
|
|
||||||
self.poly_extend_active + self.poly_space)
|
|
||||||
|
|
||||||
total_height = tx_height + min_channel + 2 * self.top_bottom_space
|
total_height = tx_height + min_channel + 2 * self.top_bottom_space
|
||||||
# debug.check(self.height > total_height,
|
# debug.check(self.height > total_height,
|
||||||
# "Cell height {0} too small for simple min height {1}.".format(self.height,
|
# "Cell height {0} too small for simple min height {1}.".format(self.height,
|
||||||
|
|
@ -202,30 +194,22 @@ class pinv(pgate.pgate):
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
|
add_source_contact=self.route_layer,
|
||||||
|
add_drain_contact=self.route_layer,
|
||||||
connect_poly=True,
|
connect_poly=True,
|
||||||
connect_active=True)
|
connect_drain_active=True)
|
||||||
self.add_mod(self.nmos)
|
self.add_mod(self.nmos)
|
||||||
|
|
||||||
self.pmos = factory.create(module_type="ptx",
|
self.pmos = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
|
add_source_contact=self.route_layer,
|
||||||
|
add_drain_contact=self.route_layer,
|
||||||
connect_poly=True,
|
connect_poly=True,
|
||||||
connect_active=True)
|
connect_drain_active=True)
|
||||||
self.add_mod(self.pmos)
|
self.add_mod(self.pmos)
|
||||||
|
|
||||||
def route_supply_rails(self):
|
|
||||||
""" Add vdd/gnd rails to the top and bottom. """
|
|
||||||
self.add_layout_pin_rect_center(text="gnd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vector(0.5 * self.width, 0),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
self.add_layout_pin_rect_center(text="vdd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vector(0.5 * self.width, self.height),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
def create_ptx(self):
|
def create_ptx(self):
|
||||||
"""
|
"""
|
||||||
Create the PMOS and NMOS netlist.
|
Create the PMOS and NMOS netlist.
|
||||||
|
|
@ -274,24 +258,16 @@ class pinv(pgate.pgate):
|
||||||
# Pick point at right most of NMOS and connect down to PMOS
|
# Pick point at right most of NMOS and connect down to PMOS
|
||||||
nmos_drain_pos = nmos_drain_pin.bc()
|
nmos_drain_pos = nmos_drain_pin.bc()
|
||||||
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
|
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
|
||||||
self.add_path("m1", [nmos_drain_pos, pmos_drain_pos])
|
self.add_path(self.route_layer, [nmos_drain_pos, pmos_drain_pos])
|
||||||
|
|
||||||
# Remember the mid for the output
|
# Remember the mid for the output
|
||||||
mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y)
|
mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y)
|
||||||
|
|
||||||
if self.route_output:
|
# This leaves the output as an internal pin (min sized)
|
||||||
# This extends the output to the edge of the cell
|
output_offset = mid_drain_offset + vector(0.5 * self.route_layer_width, 0)
|
||||||
output_offset = mid_drain_offset.scale(0, 1) + vector(self.width, 0)
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
self.add_layout_pin_segment_center(text="Z",
|
layer=self.route_layer,
|
||||||
layer="m1",
|
offset=output_offset)
|
||||||
start=mid_drain_offset,
|
|
||||||
end=output_offset)
|
|
||||||
else:
|
|
||||||
# This leaves the output as an internal pin (min sized)
|
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
|
||||||
layer="m1",
|
|
||||||
offset=mid_drain_offset \
|
|
||||||
+ vector(0.5 * self.m1_width, 0))
|
|
||||||
|
|
||||||
def add_well_contacts(self):
|
def add_well_contacts(self):
|
||||||
""" Add n/p well taps to the layout and connect to supplies """
|
""" Add n/p well taps to the layout and connect to supplies """
|
||||||
|
|
|
||||||
|
|
@ -72,65 +72,45 @@ class pnand2(pgate.pgate):
|
||||||
|
|
||||||
def add_ptx(self):
|
def add_ptx(self):
|
||||||
""" Create the PMOS and NMOS transistors. """
|
""" Create the PMOS and NMOS transistors. """
|
||||||
self.nmos_nd = factory.create(module_type="ptx",
|
self.nmos_left = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_drain_contact=False,
|
add_source_contact=self.route_layer,
|
||||||
connect_poly=True,
|
add_drain_contact="active")
|
||||||
connect_active=True)
|
self.add_mod(self.nmos_left)
|
||||||
self.add_mod(self.nmos_nd)
|
|
||||||
|
|
||||||
self.nmos_ns = factory.create(module_type="ptx",
|
self.nmos_right = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact=False,
|
add_source_contact="active",
|
||||||
connect_poly=True,
|
add_drain_contact=self.route_layer)
|
||||||
connect_active=True)
|
self.add_mod(self.nmos_right)
|
||||||
self.add_mod(self.nmos_ns)
|
|
||||||
|
|
||||||
self.pmos = factory.create(module_type="ptx",
|
self.pmos_left = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
connect_poly=True,
|
add_source_contact=self.route_layer,
|
||||||
connect_active=True)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos)
|
self.add_mod(self.pmos_left)
|
||||||
|
|
||||||
|
self.pmos_right = factory.create(module_type="ptx",
|
||||||
|
width=self.pmos_width,
|
||||||
|
mults=self.tx_mults,
|
||||||
|
tx_type="pmos",
|
||||||
|
add_source_contact=self.route_layer,
|
||||||
|
add_drain_contact=self.route_layer)
|
||||||
|
self.add_mod(self.pmos_right)
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Pre-compute some handy layout parameters. """
|
""" Pre-compute some handy layout parameters. """
|
||||||
|
|
||||||
# metal spacing to allow contacts on any layer
|
|
||||||
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
|
|
||||||
self.m1_space + contact.m1_via.first_layer_width,
|
|
||||||
self.m2_space + contact.m2_via.first_layer_width,
|
|
||||||
self.m3_space + contact.m2_via.second_layer_width)
|
|
||||||
|
|
||||||
|
|
||||||
# Compute the other pmos2 location,
|
# Compute the other pmos2 location,
|
||||||
# but determining offset to overlap the
|
# but determining offset to overlap the
|
||||||
# source and drain pins
|
# source and drain pins
|
||||||
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
self.overlap_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center()
|
||||||
|
|
||||||
# This is the extra space needed to ensure DRC rules
|
|
||||||
# to the active contacts
|
|
||||||
extra_contact_space = max(-self.nmos_nd.get_pin("D").by(), 0)
|
|
||||||
# This is a poly-to-poly of a flipped cell
|
|
||||||
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
|
|
||||||
self.poly_extend_active + self.poly_space)
|
|
||||||
|
|
||||||
def route_supply_rails(self):
|
|
||||||
""" Add vdd/gnd rails to the top and bottom. """
|
|
||||||
self.add_layout_pin_rect_center(text="gnd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vector(0.5*self.width, 0),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
self.add_layout_pin_rect_center(text="vdd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vector(0.5 * self.width, self.height),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
def create_ptx(self):
|
def create_ptx(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -138,19 +118,19 @@ class pnand2(pgate.pgate):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.pmos1_inst = self.add_inst(name="pnand2_pmos1",
|
self.pmos1_inst = self.add_inst(name="pnand2_pmos1",
|
||||||
mod=self.pmos)
|
mod=self.pmos_left)
|
||||||
self.connect_inst(["vdd", "A", "Z", "vdd"])
|
self.connect_inst(["vdd", "A", "Z", "vdd"])
|
||||||
|
|
||||||
self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
|
self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
|
||||||
mod=self.pmos)
|
mod=self.pmos_right)
|
||||||
self.connect_inst(["Z", "B", "vdd", "vdd"])
|
self.connect_inst(["Z", "B", "vdd", "vdd"])
|
||||||
|
|
||||||
self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
|
self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
|
||||||
mod=self.nmos_nd)
|
mod=self.nmos_left)
|
||||||
self.connect_inst(["Z", "B", "net1", "gnd"])
|
self.connect_inst(["Z", "B", "net1", "gnd"])
|
||||||
|
|
||||||
self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
|
self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
|
||||||
mod=self.nmos_ns)
|
mod=self.nmos_right)
|
||||||
self.connect_inst(["net1", "A", "gnd", "gnd"])
|
self.connect_inst(["net1", "A", "gnd", "gnd"])
|
||||||
|
|
||||||
def place_ptx(self):
|
def place_ptx(self):
|
||||||
|
|
@ -159,35 +139,29 @@ class pnand2(pgate.pgate):
|
||||||
to provide maximum routing in channel
|
to provide maximum routing in channel
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pmos1_pos = vector(self.pmos.active_offset.x,
|
pmos1_pos = vector(self.pmos_left.active_offset.x,
|
||||||
self.height - self.pmos.active_height \
|
self.height - self.pmos_left.active_height \
|
||||||
- self.top_bottom_space)
|
- self.top_bottom_space)
|
||||||
self.pmos1_inst.place(pmos1_pos)
|
self.pmos1_inst.place(pmos1_pos)
|
||||||
|
|
||||||
self.pmos2_pos = pmos1_pos + self.overlap_offset
|
self.pmos2_pos = pmos1_pos + self.overlap_offset
|
||||||
self.pmos2_inst.place(self.pmos2_pos)
|
self.pmos2_inst.place(self.pmos2_pos)
|
||||||
|
|
||||||
nmos1_pos = vector(self.pmos.active_offset.x,
|
nmos1_pos = vector(self.pmos_left.active_offset.x,
|
||||||
self.top_bottom_space)
|
self.top_bottom_space)
|
||||||
self.nmos1_inst.place(nmos1_pos)
|
self.nmos1_inst.place(nmos1_pos)
|
||||||
|
|
||||||
self.nmos2_pos = nmos1_pos + self.overlap_offset
|
self.nmos2_pos = nmos1_pos + self.overlap_offset
|
||||||
self.nmos2_inst.place(self.nmos2_pos)
|
self.nmos2_inst.place(self.nmos2_pos)
|
||||||
|
|
||||||
# Output position will be in between the PMOS and NMOS
|
|
||||||
self.output_pos = vector(0,
|
|
||||||
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height))
|
|
||||||
|
|
||||||
def add_well_contacts(self):
|
def add_well_contacts(self):
|
||||||
"""
|
"""
|
||||||
Add n/p well taps to the layout and connect to supplies
|
Add n/p well taps to the layout and connect to supplies
|
||||||
AFTER the wells are created
|
AFTER the wells are created
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.add_nwell_contact(self.pmos,
|
self.add_nwell_contact(self.pmos_right, self.pmos2_pos)
|
||||||
self.pmos2_pos + vector(self.m1_pitch, 0))
|
self.add_pwell_contact(self.nmos_left, self.nmos2_pos)
|
||||||
self.add_pwell_contact(self.nmos_nd,
|
|
||||||
self.nmos2_pos + vector(self.m1_pitch, 0))
|
|
||||||
|
|
||||||
def connect_rails(self):
|
def connect_rails(self):
|
||||||
""" Connect the nmos and pmos to its respective power rails """
|
""" Connect the nmos and pmos to its respective power rails """
|
||||||
|
|
@ -200,35 +174,46 @@ class pnand2(pgate.pgate):
|
||||||
|
|
||||||
def route_inputs(self):
|
def route_inputs(self):
|
||||||
""" Route the A and B inputs """
|
""" Route the A and B inputs """
|
||||||
inputB_yoffset = self.nmos2_inst.uy() + 0.5 * contact.poly_contact.height
|
|
||||||
|
|
||||||
|
# Top of NMOS drain
|
||||||
|
nmos_pin = self.nmos2_inst.get_pin("D")
|
||||||
|
bottom_pin_offset = nmos_pin.uy()
|
||||||
|
self.inputA_yoffset = bottom_pin_offset + self.m1_pitch
|
||||||
|
|
||||||
|
self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch
|
||||||
|
|
||||||
|
# This will help with the wells and the input/output placement
|
||||||
self.route_input_gate(self.pmos2_inst,
|
self.route_input_gate(self.pmos2_inst,
|
||||||
self.nmos2_inst,
|
self.nmos2_inst,
|
||||||
inputB_yoffset,
|
self.inputB_yoffset,
|
||||||
"B",
|
"B",
|
||||||
position="center")
|
position="center")
|
||||||
|
|
||||||
# This will help with the wells and the input/output placement
|
|
||||||
self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \
|
|
||||||
- contact.poly_contact.height
|
|
||||||
self.route_input_gate(self.pmos1_inst,
|
self.route_input_gate(self.pmos1_inst,
|
||||||
self.nmos1_inst,
|
self.nmos1_inst,
|
||||||
self.inputA_yoffset,
|
self.inputA_yoffset,
|
||||||
"A")
|
"A",
|
||||||
|
position="center")
|
||||||
|
|
||||||
def route_output(self):
|
def route_output(self):
|
||||||
""" Route the Z output """
|
""" Route the Z output """
|
||||||
|
|
||||||
|
# One routing track layer below the PMOS contacts
|
||||||
|
route_layer_offset = 0.5 * self.route_layer_width + self.route_layer_space
|
||||||
|
output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset
|
||||||
|
|
||||||
|
|
||||||
# PMOS1 drain
|
# PMOS1 drain
|
||||||
pmos_pin = self.pmos1_inst.get_pin("D")
|
pmos_pin = self.pmos1_inst.get_pin("D")
|
||||||
top_pin_offset = pmos_pin.center()
|
top_pin_offset = pmos_pin.bc()
|
||||||
# NMOS2 drain
|
# NMOS2 drain
|
||||||
nmos_pin = self.nmos2_inst.get_pin("D")
|
nmos_pin = self.nmos2_inst.get_pin("D")
|
||||||
bottom_pin_offset = nmos_pin.center()
|
bottom_pin_offset = nmos_pin.uc()
|
||||||
|
|
||||||
# Output pin
|
# Output pin
|
||||||
c_pin = self.get_pin("B")
|
out_offset = vector(nmos_pin.cx() + self.route_layer_pitch,
|
||||||
out_offset = vector(c_pin.cx() + self.m1_pitch,
|
output_yoffset)
|
||||||
self.inputA_yoffset)
|
|
||||||
|
|
||||||
# This routes on M2
|
# This routes on M2
|
||||||
# # Midpoints of the L routes go horizontal first then vertical
|
# # Midpoints of the L routes go horizontal first then vertical
|
||||||
|
|
@ -251,27 +236,22 @@ class pnand2(pgate.pgate):
|
||||||
# [top_pin_offset, mid1_offset, out_offset,
|
# [top_pin_offset, mid1_offset, out_offset,
|
||||||
# mid2_offset, bottom_pin_offset])
|
# mid2_offset, bottom_pin_offset])
|
||||||
|
|
||||||
# This routes on M1
|
# This routes on route_layer
|
||||||
# Midpoints of the L routes goes vertical first then horizontal
|
# Midpoints of the L routes goes vertical first then horizontal
|
||||||
mid1_offset = vector(top_pin_offset.x, out_offset.y)
|
top_mid_offset = vector(top_pin_offset.x, out_offset.y)
|
||||||
# Midpoints of the L routes goes horizontal first then vertical
|
# Top transistors
|
||||||
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
|
self.add_path(self.route_layer,
|
||||||
|
[top_pin_offset, top_mid_offset, out_offset])
|
||||||
|
|
||||||
self.add_path("m1",
|
bottom_mid_offset = bottom_pin_offset + vector(0, self.route_layer_pitch)
|
||||||
[top_pin_offset, mid1_offset, out_offset])
|
# Bottom transistors
|
||||||
# Route in two segments to have the width rule
|
self.add_path(self.route_layer,
|
||||||
self.add_path("m1",
|
[out_offset, bottom_mid_offset, bottom_pin_offset])
|
||||||
[bottom_pin_offset, mid2_offset + vector(0.5 * self.m1_width, 0)],
|
|
||||||
width=nmos_pin.height())
|
|
||||||
self.add_path("m1",
|
|
||||||
[mid2_offset, out_offset])
|
|
||||||
|
|
||||||
# This extends the output to the edge of the cell
|
# This extends the output to the edge of the cell
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
layer="m1",
|
layer=self.route_layer,
|
||||||
offset=out_offset,
|
offset=out_offset)
|
||||||
width=contact.m1_via.first_layer_width,
|
|
||||||
height=contact.m1_via.first_layer_height)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import contact
|
|
||||||
import pgate
|
import pgate
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
|
|
@ -14,6 +13,7 @@ import logical_effort
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class pnand3(pgate.pgate):
|
class pnand3(pgate.pgate):
|
||||||
"""
|
"""
|
||||||
This module generates gds of a parametrically sized 2-input nand.
|
This module generates gds of a parametrically sized 2-input nand.
|
||||||
|
|
@ -74,68 +74,64 @@ class pnand3(pgate.pgate):
|
||||||
|
|
||||||
def add_ptx(self):
|
def add_ptx(self):
|
||||||
""" Create the PMOS and NMOS transistors. """
|
""" Create the PMOS and NMOS transistors. """
|
||||||
self.nmos_nsnd = factory.create(module_type="ptx",
|
self.nmos_center = factory.create(module_type="ptx",
|
||||||
|
width=self.nmos_width,
|
||||||
|
mults=self.tx_mults,
|
||||||
|
tx_type="nmos",
|
||||||
|
add_source_contact="active",
|
||||||
|
add_drain_contact="active")
|
||||||
|
self.add_mod(self.nmos_center)
|
||||||
|
|
||||||
|
self.nmos_right = factory.create(module_type="ptx",
|
||||||
|
width=self.nmos_width,
|
||||||
|
mults=self.tx_mults,
|
||||||
|
tx_type="nmos",
|
||||||
|
add_source_contact="active",
|
||||||
|
add_drain_contact=self.route_layer)
|
||||||
|
self.add_mod(self.nmos_right)
|
||||||
|
|
||||||
|
self.nmos_left = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact=False,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=False,
|
add_drain_contact="active")
|
||||||
connect_poly=True,
|
self.add_mod(self.nmos_left)
|
||||||
connect_active=True)
|
|
||||||
self.add_mod(self.nmos_nsnd)
|
|
||||||
|
|
||||||
self.nmos_ns = factory.create(module_type="ptx",
|
self.pmos_left = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.pmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="nmos",
|
tx_type="pmos",
|
||||||
add_source_contact=False,
|
add_source_contact=self.route_layer,
|
||||||
connect_poly=True,
|
add_drain_contact=self.route_layer)
|
||||||
connect_active=True)
|
self.add_mod(self.pmos_left)
|
||||||
self.add_mod(self.nmos_ns)
|
|
||||||
|
|
||||||
self.nmos_nd = factory.create(module_type="ptx",
|
self.pmos_center = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.pmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="nmos",
|
tx_type="pmos",
|
||||||
add_drain_contact=False,
|
add_source_contact=self.route_layer,
|
||||||
connect_poly=True,
|
add_drain_contact=self.route_layer)
|
||||||
connect_active=True)
|
self.add_mod(self.pmos_center)
|
||||||
self.add_mod(self.nmos_nd)
|
|
||||||
|
|
||||||
self.pmos = factory.create(module_type="ptx",
|
self.pmos_right = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
connect_poly=True,
|
add_source_contact=self.route_layer,
|
||||||
connect_active=True)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos)
|
self.add_mod(self.pmos_right)
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Pre-compute some handy layout parameters. """
|
""" Pre-compute some handy layout parameters. """
|
||||||
|
|
||||||
# Compute the overlap of the source and drain pins
|
# Compute the overlap of the source and drain pins
|
||||||
overlap_xoffset = self.pmos.get_pin("D").ll().x - self.pmos.get_pin("S").ll().x
|
self.ptx_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center()
|
||||||
self.ptx_offset = vector(overlap_xoffset, 0)
|
|
||||||
|
|
||||||
# This is the extra space needed to ensure DRC rules
|
# This is the extra space needed to ensure DRC rules
|
||||||
# to the active contacts
|
# to the active contacts
|
||||||
nmos = factory.create(module_type="ptx", tx_type="nmos")
|
nmos = factory.create(module_type="ptx", tx_type="nmos")
|
||||||
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
|
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
|
||||||
# This is a poly-to-poly of a flipped cell
|
|
||||||
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
|
|
||||||
self.poly_extend_active + self.poly_space)
|
|
||||||
|
|
||||||
def route_supply_rails(self):
|
|
||||||
""" Add vdd/gnd rails to the top and bottom. """
|
|
||||||
self.add_layout_pin_rect_center(text="gnd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vector(0.5 * self.width, 0),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
self.add_layout_pin_rect_center(text="vdd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vector(0.5 * self.width, self.height),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
def create_ptx(self):
|
def create_ptx(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -143,27 +139,27 @@ class pnand3(pgate.pgate):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.pmos1_inst = self.add_inst(name="pnand3_pmos1",
|
self.pmos1_inst = self.add_inst(name="pnand3_pmos1",
|
||||||
mod=self.pmos)
|
mod=self.pmos_left)
|
||||||
self.connect_inst(["vdd", "A", "Z", "vdd"])
|
self.connect_inst(["vdd", "A", "Z", "vdd"])
|
||||||
|
|
||||||
self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
|
self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
|
||||||
mod=self.pmos)
|
mod=self.pmos_center)
|
||||||
self.connect_inst(["Z", "B", "vdd", "vdd"])
|
self.connect_inst(["Z", "B", "vdd", "vdd"])
|
||||||
|
|
||||||
self.pmos3_inst = self.add_inst(name="pnand3_pmos3",
|
self.pmos3_inst = self.add_inst(name="pnand3_pmos3",
|
||||||
mod=self.pmos)
|
mod=self.pmos_right)
|
||||||
self.connect_inst(["Z", "C", "vdd", "vdd"])
|
self.connect_inst(["Z", "C", "vdd", "vdd"])
|
||||||
|
|
||||||
self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
|
self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
|
||||||
mod=self.nmos_nd)
|
mod=self.nmos_left)
|
||||||
self.connect_inst(["Z", "C", "net1", "gnd"])
|
self.connect_inst(["Z", "C", "net1", "gnd"])
|
||||||
|
|
||||||
self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
|
self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
|
||||||
mod=self.nmos_nsnd)
|
mod=self.nmos_center)
|
||||||
self.connect_inst(["net1", "B", "net2", "gnd"])
|
self.connect_inst(["net1", "B", "net2", "gnd"])
|
||||||
|
|
||||||
self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
|
self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
|
||||||
mod=self.nmos_ns)
|
mod=self.nmos_right)
|
||||||
self.connect_inst(["net2", "A", "gnd", "gnd"])
|
self.connect_inst(["net2", "A", "gnd", "gnd"])
|
||||||
|
|
||||||
def place_ptx(self):
|
def place_ptx(self):
|
||||||
|
|
@ -172,8 +168,8 @@ class pnand3(pgate.pgate):
|
||||||
and lowest position to provide maximum routing in channel
|
and lowest position to provide maximum routing in channel
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pmos1_pos = vector(self.pmos.active_offset.x,
|
pmos1_pos = vector(self.pmos_left.active_offset.x,
|
||||||
self.height - self.pmos.active_height - self.top_bottom_space)
|
self.height - self.pmos_left.active_height - self.top_bottom_space)
|
||||||
self.pmos1_inst.place(pmos1_pos)
|
self.pmos1_inst.place(pmos1_pos)
|
||||||
|
|
||||||
pmos2_pos = pmos1_pos + self.ptx_offset
|
pmos2_pos = pmos1_pos + self.ptx_offset
|
||||||
|
|
@ -182,7 +178,7 @@ class pnand3(pgate.pgate):
|
||||||
self.pmos3_pos = pmos2_pos + self.ptx_offset
|
self.pmos3_pos = pmos2_pos + self.ptx_offset
|
||||||
self.pmos3_inst.place(self.pmos3_pos)
|
self.pmos3_inst.place(self.pmos3_pos)
|
||||||
|
|
||||||
nmos1_pos = vector(self.pmos.active_offset.x,
|
nmos1_pos = vector(self.pmos_left.active_offset.x,
|
||||||
self.top_bottom_space)
|
self.top_bottom_space)
|
||||||
self.nmos1_inst.place(nmos1_pos)
|
self.nmos1_inst.place(nmos1_pos)
|
||||||
|
|
||||||
|
|
@ -195,9 +191,9 @@ class pnand3(pgate.pgate):
|
||||||
def add_well_contacts(self):
|
def add_well_contacts(self):
|
||||||
""" Add n/p well taps to the layout and connect to supplies """
|
""" Add n/p well taps to the layout and connect to supplies """
|
||||||
|
|
||||||
self.add_nwell_contact(self.pmos,
|
self.add_nwell_contact(self.pmos_right,
|
||||||
self.pmos3_pos + vector(self.m1_pitch, 0))
|
self.pmos3_pos + vector(self.m1_pitch, 0))
|
||||||
self.add_pwell_contact(self.nmos_ns,
|
self.add_pwell_contact(self.nmos_right,
|
||||||
self.nmos3_pos + vector(self.m1_pitch, 0))
|
self.nmos3_pos + vector(self.m1_pitch, 0))
|
||||||
|
|
||||||
def connect_rails(self):
|
def connect_rails(self):
|
||||||
|
|
@ -212,34 +208,31 @@ class pnand3(pgate.pgate):
|
||||||
def route_inputs(self):
|
def route_inputs(self):
|
||||||
""" Route the A and B and C inputs """
|
""" Route the A and B and C inputs """
|
||||||
|
|
||||||
m1_pitch = self.m1_space + contact.m1_via.first_layer_height
|
pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
|
||||||
|
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
|
||||||
|
|
||||||
|
self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space
|
||||||
|
self.route_input_gate(self.pmos1_inst,
|
||||||
|
self.nmos1_inst,
|
||||||
|
self.inputA_yoffset,
|
||||||
|
"A",
|
||||||
|
position="left")
|
||||||
|
|
||||||
# Put B right on the well line
|
# Put B right on the well line
|
||||||
self.inputB_yoffset = self.nwell_y_offset
|
self.inputB_yoffset = self.inputA_yoffset - self.m1_pitch
|
||||||
self.route_input_gate(self.pmos2_inst,
|
self.route_input_gate(self.pmos2_inst,
|
||||||
self.nmos2_inst,
|
self.nmos2_inst,
|
||||||
self.inputB_yoffset,
|
self.inputB_yoffset,
|
||||||
"B",
|
"B",
|
||||||
position="center")
|
position="center")
|
||||||
|
|
||||||
# FIXME: constant hack
|
self.inputC_yoffset = self.inputB_yoffset - self.m1_pitch
|
||||||
self.inputC_yoffset = self.inputB_yoffset - 1.15 * m1_pitch
|
|
||||||
self.route_input_gate(self.pmos3_inst,
|
self.route_input_gate(self.pmos3_inst,
|
||||||
self.nmos3_inst,
|
self.nmos3_inst,
|
||||||
self.inputC_yoffset,
|
self.inputC_yoffset,
|
||||||
"C",
|
"C",
|
||||||
position="right")
|
position="right")
|
||||||
|
|
||||||
# FIXME: constant hack
|
|
||||||
if OPTS.tech_name == "s8":
|
|
||||||
self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch
|
|
||||||
else:
|
|
||||||
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch
|
|
||||||
self.route_input_gate(self.pmos1_inst,
|
|
||||||
self.nmos1_inst,
|
|
||||||
self.inputA_yoffset,
|
|
||||||
"A",
|
|
||||||
position="left")
|
|
||||||
|
|
||||||
def route_output(self):
|
def route_output(self):
|
||||||
""" Route the Z output """
|
""" Route the Z output """
|
||||||
|
|
||||||
|
|
@ -250,13 +243,8 @@ class pnand3(pgate.pgate):
|
||||||
# NMOS3 drain
|
# NMOS3 drain
|
||||||
nmos3_pin = self.nmos3_inst.get_pin("D")
|
nmos3_pin = self.nmos3_inst.get_pin("D")
|
||||||
|
|
||||||
# midpoint for routing
|
out_offset = vector(nmos3_pin.cx() + self.route_layer_pitch,
|
||||||
mid_offset = vector(nmos3_pin.cx() + self.m1_pitch,
|
self.output_yoffset)
|
||||||
self.inputA_yoffset)
|
|
||||||
|
|
||||||
# Aligned with the well taps
|
|
||||||
out_offset = vector(self.nwell_contact.cx(),
|
|
||||||
self.inputA_yoffset)
|
|
||||||
|
|
||||||
# Go up to metal2 for ease on all output pins
|
# Go up to metal2 for ease on all output pins
|
||||||
# self.add_via_center(layers=self.m1_stack,
|
# self.add_via_center(layers=self.m1_stack,
|
||||||
|
|
@ -282,26 +270,24 @@ class pnand3(pgate.pgate):
|
||||||
bottom_pin_offset = nmos3_pin.center()
|
bottom_pin_offset = nmos3_pin.center()
|
||||||
|
|
||||||
# PMOS1 to output
|
# PMOS1 to output
|
||||||
self.add_path("m1", [top_left_pin_offset,
|
self.add_path(self.route_layer, [top_left_pin_offset,
|
||||||
vector(top_left_pin_offset.x, out_offset.y),
|
vector(top_left_pin_offset.x, out_offset.y),
|
||||||
out_offset])
|
out_offset])
|
||||||
# PMOS3 to output
|
# PMOS3 to output
|
||||||
self.add_path("m1", [top_right_pin_offset,
|
self.add_path(self.route_layer, [top_right_pin_offset,
|
||||||
vector(top_right_pin_offset.x, mid_offset.y),
|
vector(top_right_pin_offset.x, out_offset.y),
|
||||||
mid_offset])
|
out_offset])
|
||||||
# NMOS3 to output
|
# NMOS3 to output
|
||||||
mid2_offset = vector(mid_offset.x, bottom_pin_offset.y)
|
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
|
||||||
self.add_path("m1",
|
self.add_path(self.route_layer,
|
||||||
[bottom_pin_offset, mid2_offset],
|
[bottom_pin_offset, mid2_offset],
|
||||||
width=nmos3_pin.height())
|
width=nmos3_pin.height())
|
||||||
mid3_offset = vector(mid_offset.x, nmos3_pin.by())
|
mid3_offset = vector(out_offset.x, nmos3_pin.by())
|
||||||
self.add_path("m1", [mid3_offset, mid_offset])
|
self.add_path(self.route_layer, [mid3_offset, out_offset])
|
||||||
|
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
layer="m1",
|
layer=self.route_layer,
|
||||||
offset=out_offset,
|
offset=out_offset)
|
||||||
width=contact.m1_via.first_layer_width,
|
|
||||||
height=contact.m1_via.first_layer_height)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
|
|
|
||||||
|
|
@ -71,74 +71,54 @@ class pnor2(pgate.pgate):
|
||||||
|
|
||||||
def add_ptx(self):
|
def add_ptx(self):
|
||||||
""" Create the PMOS and NMOS transistors. """
|
""" Create the PMOS and NMOS transistors. """
|
||||||
self.nmos = factory.create(module_type="ptx",
|
self.nmos_left = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
connect_poly=True,
|
add_source_contact=self.route_layer,
|
||||||
connect_active=True)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.nmos)
|
self.add_mod(self.nmos_left)
|
||||||
|
|
||||||
self.pmos_nd = factory.create(module_type="ptx",
|
self.nmos_right = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.nmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="pmos",
|
tx_type="nmos",
|
||||||
add_drain_contact=False,
|
add_source_contact=self.route_layer,
|
||||||
connect_poly=True,
|
add_drain_contact=self.route_layer)
|
||||||
connect_active=True)
|
self.add_mod(self.nmos_right)
|
||||||
self.add_mod(self.pmos_nd)
|
|
||||||
|
|
||||||
self.pmos_ns = factory.create(module_type="ptx",
|
self.pmos_left = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
mults=self.tx_mults,
|
mults=self.tx_mults,
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=False,
|
add_source_contact=self.route_layer,
|
||||||
connect_poly=True,
|
add_drain_contact="active")
|
||||||
connect_active=True)
|
self.add_mod(self.pmos_left)
|
||||||
self.add_mod(self.pmos_ns)
|
|
||||||
|
self.pmos_right = factory.create(module_type="ptx",
|
||||||
|
width=self.pmos_width,
|
||||||
|
mults=self.tx_mults,
|
||||||
|
tx_type="pmos",
|
||||||
|
add_source_contact="active",
|
||||||
|
add_drain_contact=self.route_layer)
|
||||||
|
self.add_mod(self.pmos_right)
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Pre-compute some handy layout parameters. """
|
""" Pre-compute some handy layout parameters. """
|
||||||
|
|
||||||
# metal spacing to allow contacts on any layer
|
|
||||||
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
|
|
||||||
self.m1_space + contact.m1_via.first_layer_width,
|
|
||||||
self.m2_space + contact.m2_via.first_layer_width,
|
|
||||||
self.m3_space + contact.m2_via.second_layer_width)
|
|
||||||
|
|
||||||
# Compute the other pmos2 location, but determining
|
# Compute the other pmos2 location, but determining
|
||||||
# offset to overlap the source and drain pins
|
# offset to overlap the source and drain pins
|
||||||
self.overlap_offset = self.pmos_ns.get_pin("D").ll() - self.pmos_nd.get_pin("S").ll()
|
self.overlap_offset = self.pmos_right.get_pin("D").center() - self.pmos_left.get_pin("S").center()
|
||||||
|
|
||||||
# Two PMOS devices and a well contact. Separation between each.
|
# Two PMOS devices and a well contact. Separation between each.
|
||||||
# Enclosure space on the sides.
|
# Enclosure space on the sides.
|
||||||
self.width = 2 * self.pmos_ns.active_width \
|
self.width = 2 * self.pmos_right.active_width \
|
||||||
+ self.pmos_ns.active_contact.width \
|
+ self.pmos_right.active_contact.width \
|
||||||
+ 2 * self.active_space \
|
+ 2 * self.active_space \
|
||||||
+ 0.5 * self.nwell_enclose_active
|
+ 0.5 * self.nwell_enclose_active
|
||||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||||
# Height is an input parameter, so it is not recomputed.
|
# Height is an input parameter, so it is not recomputed.
|
||||||
|
|
||||||
# This is the extra space needed to ensure DRC rules
|
|
||||||
# to the active contacts
|
|
||||||
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
|
|
||||||
# This is a poly-to-poly of a flipped cell
|
|
||||||
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
|
|
||||||
self.poly_extend_active,
|
|
||||||
self.poly_space)
|
|
||||||
|
|
||||||
def route_supply_rails(self):
|
|
||||||
""" Add vdd/gnd rails to the top and bottom. """
|
|
||||||
self.add_layout_pin_rect_center(text="gnd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vector(0.5 * self.width, 0),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
self.add_layout_pin_rect_center(text="vdd",
|
|
||||||
layer="m1",
|
|
||||||
offset=vector(0.5 * self.width, self.height),
|
|
||||||
width=self.width)
|
|
||||||
|
|
||||||
def create_ptx(self):
|
def create_ptx(self):
|
||||||
"""
|
"""
|
||||||
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
||||||
|
|
@ -146,19 +126,19 @@ class pnor2(pgate.pgate):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
|
self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
|
||||||
mod=self.pmos_nd)
|
mod=self.pmos_left)
|
||||||
self.connect_inst(["vdd", "A", "net1", "vdd"])
|
self.connect_inst(["vdd", "A", "net1", "vdd"])
|
||||||
|
|
||||||
self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
|
self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
|
||||||
mod=self.pmos_ns)
|
mod=self.pmos_right)
|
||||||
self.connect_inst(["net1", "B", "Z", "vdd"])
|
self.connect_inst(["net1", "B", "Z", "vdd"])
|
||||||
|
|
||||||
self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
|
self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
|
||||||
mod=self.nmos)
|
mod=self.nmos_left)
|
||||||
self.connect_inst(["Z", "A", "gnd", "gnd"])
|
self.connect_inst(["Z", "A", "gnd", "gnd"])
|
||||||
|
|
||||||
self.nmos2_inst = self.add_inst(name="pnor2_nmos2",
|
self.nmos2_inst = self.add_inst(name="pnor2_nmos2",
|
||||||
mod=self.nmos)
|
mod=self.nmos_right)
|
||||||
self.connect_inst(["Z", "B", "gnd", "gnd"])
|
self.connect_inst(["Z", "B", "gnd", "gnd"])
|
||||||
|
|
||||||
def place_ptx(self):
|
def place_ptx(self):
|
||||||
|
|
@ -166,30 +146,35 @@ class pnor2(pgate.pgate):
|
||||||
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
||||||
to provide maximum routing in channel
|
to provide maximum routing in channel
|
||||||
"""
|
"""
|
||||||
|
# Some of the S/D contacts may extend beyond the active,
|
||||||
|
# but this needs to be done in the gate itself
|
||||||
|
contact_extend_active_space = max(-self.nmos_right.get_pin("D").by(), 0)
|
||||||
|
# Assume the contact starts at the active edge
|
||||||
|
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space + contact_extend_active_space
|
||||||
|
# This is a poly-to-poly of a flipped cell
|
||||||
|
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
|
||||||
|
# Recompute this since it has a small txwith the added contact extend active spacing
|
||||||
|
self.top_bottom_space = max(contact_to_vdd_rail_space,
|
||||||
|
poly_to_poly_gate_space)
|
||||||
|
|
||||||
pmos1_pos = vector(self.pmos_ns.active_offset.x,
|
pmos1_pos = vector(self.pmos_right.active_offset.x,
|
||||||
self.height - self.pmos_ns.active_height \
|
self.height - self.pmos_right.active_height - self.top_bottom_space)
|
||||||
- self.top_bottom_space)
|
|
||||||
self.pmos1_inst.place(pmos1_pos)
|
self.pmos1_inst.place(pmos1_pos)
|
||||||
|
|
||||||
self.pmos2_pos = pmos1_pos + self.overlap_offset
|
self.pmos2_pos = pmos1_pos + self.overlap_offset
|
||||||
self.pmos2_inst.place(self.pmos2_pos)
|
self.pmos2_inst.place(self.pmos2_pos)
|
||||||
|
|
||||||
nmos1_pos = vector(self.pmos_ns.active_offset.x, self.top_bottom_space)
|
nmos1_pos = vector(self.pmos_right.active_offset.x, self.top_bottom_space)
|
||||||
self.nmos1_inst.place(nmos1_pos)
|
self.nmos1_inst.place(nmos1_pos)
|
||||||
|
|
||||||
self.nmos2_pos = nmos1_pos + self.overlap_offset
|
self.nmos2_pos = nmos1_pos + self.overlap_offset
|
||||||
self.nmos2_inst.place(self.nmos2_pos)
|
self.nmos2_inst.place(self.nmos2_pos)
|
||||||
|
|
||||||
# Output position will be in between the PMOS and NMOS
|
|
||||||
self.output_pos = vector(0,
|
|
||||||
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
|
|
||||||
|
|
||||||
def add_well_contacts(self):
|
def add_well_contacts(self):
|
||||||
""" Add n/p well taps to the layout and connect to supplies """
|
""" Add n/p well taps to the layout and connect to supplies """
|
||||||
|
|
||||||
self.add_nwell_contact(self.pmos_ns, self.pmos2_pos)
|
self.add_nwell_contact(self.pmos_right, self.pmos2_pos)
|
||||||
self.add_pwell_contact(self.nmos, self.nmos2_pos)
|
self.add_pwell_contact(self.nmos_right, self.nmos2_pos)
|
||||||
|
|
||||||
def connect_rails(self):
|
def connect_rails(self):
|
||||||
""" Connect the nmos and pmos to its respective power rails """
|
""" Connect the nmos and pmos to its respective power rails """
|
||||||
|
|
@ -202,53 +187,53 @@ class pnor2(pgate.pgate):
|
||||||
|
|
||||||
def route_inputs(self):
|
def route_inputs(self):
|
||||||
""" Route the A and B inputs """
|
""" Route the A and B inputs """
|
||||||
# Use M2 spaces so we can drop vias on the pins later!
|
|
||||||
inputB_yoffset = self.nmos2_inst.uy() + contact.poly_contact.height
|
# Top of NMOS drain
|
||||||
|
nmos_pin = self.nmos2_inst.get_pin("D")
|
||||||
|
bottom_pin_offset = nmos_pin.uy()
|
||||||
|
self.inputB_yoffset = bottom_pin_offset + self.m1_nonpref_pitch
|
||||||
|
self.inputA_yoffset = self.inputB_yoffset + self.m1_nonpref_pitch
|
||||||
|
|
||||||
self.route_input_gate(self.pmos2_inst,
|
self.route_input_gate(self.pmos2_inst,
|
||||||
self.nmos2_inst,
|
self.nmos2_inst,
|
||||||
inputB_yoffset,
|
self.inputB_yoffset,
|
||||||
"B",
|
"B",
|
||||||
position="center")
|
position="right",
|
||||||
|
directions=("V", "V"))
|
||||||
|
|
||||||
# This will help with the wells and the input/output placement
|
# This will help with the wells and the input/output placement
|
||||||
self.inputA_yoffset = inputB_yoffset + self.input_spacing
|
|
||||||
self.route_input_gate(self.pmos1_inst,
|
self.route_input_gate(self.pmos1_inst,
|
||||||
self.nmos1_inst,
|
self.nmos1_inst,
|
||||||
self.inputA_yoffset,
|
self.inputA_yoffset,
|
||||||
"A")
|
"A",
|
||||||
|
directions=("V", "V"))
|
||||||
|
|
||||||
|
self.output_yoffset = self.inputA_yoffset + self.m1_nonpref_pitch
|
||||||
|
|
||||||
def route_output(self):
|
def route_output(self):
|
||||||
""" Route the Z output """
|
""" Route the Z output """
|
||||||
# PMOS2 drain
|
# PMOS2 (right) drain
|
||||||
pmos_pin = self.pmos2_inst.get_pin("D")
|
pmos_pin = self.pmos2_inst.get_pin("D")
|
||||||
# NMOS1 drain
|
# NMOS1 (left) drain
|
||||||
nmos_pin = self.nmos1_inst.get_pin("D")
|
nmos_pin = self.nmos1_inst.get_pin("D")
|
||||||
# NMOS2 drain (for output via placement)
|
# NMOS2 (right) drain (for output via placement)
|
||||||
nmos2_pin = self.nmos2_inst.get_pin("D")
|
nmos2_pin = self.nmos2_inst.get_pin("D")
|
||||||
|
|
||||||
# Go up to metal2 for ease on all output pins
|
# Go up to metal2 for ease on all output pins
|
||||||
self.add_via_center(layers=self.m1_stack,
|
# self.add_via_center(layers=self.m1_stack,
|
||||||
offset=pmos_pin.center())
|
# offset=pmos_pin.center())
|
||||||
m1m2_contact = self.add_via_center(layers=self.m1_stack,
|
# m1m2_contact = self.add_via_center(layers=self.m1_stack,
|
||||||
offset=nmos_pin.center())
|
# offset=nmos_pin.center())
|
||||||
|
|
||||||
mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y)
|
mid1_offset = vector(nmos_pin.center().x, self.output_yoffset)
|
||||||
mid2_offset = vector(pmos_pin.center().x, self.inputA_yoffset)
|
mid2_offset = vector(pmos_pin.center().x, self.output_yoffset)
|
||||||
mid3_offset = mid2_offset + vector(self.m2_width, 0)
|
|
||||||
|
|
||||||
# PMOS1 to mid-drain to NMOS2 drain
|
# PMOS1 to mid-drain to NMOS2 drain
|
||||||
self.add_path("m2",
|
self.add_path(self.route_layer,
|
||||||
[pmos_pin.center(), mid2_offset, mid3_offset])
|
[nmos_pin.center(), mid1_offset, mid2_offset, pmos_pin.center()])
|
||||||
self.add_path("m2",
|
|
||||||
[nmos_pin.rc(), mid1_offset, mid2_offset])
|
|
||||||
# This extends the output to the edge of the cell
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=mid3_offset)
|
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
layer="m1",
|
layer=self.route_layer,
|
||||||
offset=mid3_offset,
|
offset=mid2_offset)
|
||||||
width=contact.m1_via.first_layer_height,
|
|
||||||
height=contact.m1_via.first_layer_width)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ class precharge(design.design):
|
||||||
Initializes the upper and lower pmos
|
Initializes the upper and lower pmos
|
||||||
"""
|
"""
|
||||||
if(OPTS.tech_name == "s8"):
|
if(OPTS.tech_name == "s8"):
|
||||||
(self.ptx_width, self.ptx_mults) = pgate.bin_width(self, "pmos", self.ptx_width)
|
(self.ptx_width, self.ptx_mults) = pgate.bin_width("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,
|
||||||
|
|
@ -114,7 +114,7 @@ class precharge(design.design):
|
||||||
|
|
||||||
self.add_power_pin("vdd",
|
self.add_power_pin("vdd",
|
||||||
self.well_contact_pos,
|
self.well_contact_pos,
|
||||||
vertical=True)
|
directions=("V", "V"))
|
||||||
|
|
||||||
# Hack for li layers
|
# Hack for li layers
|
||||||
if hasattr(self, "li_stack"):
|
if hasattr(self, "li_stack"):
|
||||||
|
|
@ -159,7 +159,7 @@ class precharge(design.design):
|
||||||
self.lower_pmos_inst.place(self.lower_pmos_position)
|
self.lower_pmos_inst.place(self.lower_pmos_position)
|
||||||
|
|
||||||
# adds the upper pmos(s) to layout with 2 M2 tracks
|
# adds the upper pmos(s) to layout with 2 M2 tracks
|
||||||
ydiff = self.pmos.height + self.m2_pitch
|
ydiff = self.pmos.height + 2 * self.m2_pitch
|
||||||
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
|
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
|
||||||
self.upper_pmos1_inst.place(self.upper_pmos1_pos)
|
self.upper_pmos1_inst.place(self.upper_pmos1_pos)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,6 @@ class ptristate_inv(pgate.pgate):
|
||||||
self.width = self.well_width + 0.5 * self.m1_space
|
self.width = self.well_width + 0.5 * self.m1_space
|
||||||
# Height is an input parameter, so it is not recomputed.
|
# Height is an input parameter, so it is not recomputed.
|
||||||
|
|
||||||
# Make sure we can put a well above and below
|
|
||||||
self.top_bottom_space = max(contact.active_contact.width, contact.active_contact.height)
|
|
||||||
|
|
||||||
def add_ptx(self):
|
def add_ptx(self):
|
||||||
""" Create the PMOS and NMOS transistors. """
|
""" Create the PMOS and NMOS transistors. """
|
||||||
self.nmos = factory.create(module_type="ptx",
|
self.nmos = factory.create(module_type="ptx",
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import contact
|
||||||
import logical_effort
|
import logical_effort
|
||||||
import os
|
import os
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
from pgate import pgate
|
||||||
|
|
||||||
class ptx(design.design):
|
class ptx(design.design):
|
||||||
"""
|
"""
|
||||||
|
|
@ -23,32 +23,47 @@ class ptx(design.design):
|
||||||
the transistor width. Mults is the number of transistors of the
|
the transistor width. Mults is the number of transistors of the
|
||||||
given width. Total width is therefore mults*width. Options allow
|
given width. Total width is therefore mults*width. Options allow
|
||||||
you to connect the fingered gates and active for parallel devices.
|
you to connect the fingered gates and active for parallel devices.
|
||||||
|
The add_*_contact option tells which layer to bring source/drain up to.
|
||||||
"""
|
"""
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
name="",
|
name="",
|
||||||
width=drc("minwidth_tx"),
|
width=drc("minwidth_tx"),
|
||||||
mults=1,
|
mults=1,
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact=True,
|
add_source_contact=None,
|
||||||
add_drain_contact=True,
|
add_drain_contact=None,
|
||||||
series_devices=False,
|
series_devices=False,
|
||||||
connect_active=False,
|
connect_drain_active=False,
|
||||||
|
connect_source_active=False,
|
||||||
connect_poly=False,
|
connect_poly=False,
|
||||||
num_contacts=None):
|
num_contacts=None):
|
||||||
|
|
||||||
|
if "li" in layer:
|
||||||
|
self.route_layer = "li"
|
||||||
|
else:
|
||||||
|
self.route_layer = "m1"
|
||||||
|
|
||||||
|
# Default contacts are the lowest layer
|
||||||
|
if not add_source_contact:
|
||||||
|
add_source_contact = self.route_layer
|
||||||
|
|
||||||
|
# Default contacts are the lowest layer
|
||||||
|
if not add_drain_contact:
|
||||||
|
add_drain_contact = self.route_layer
|
||||||
|
|
||||||
# We need to keep unique names because outputting to GDSII
|
# We need to keep unique names because outputting to GDSII
|
||||||
# will use the last record with a given name. I.e., you will
|
# will use the last record with a given name. I.e., you will
|
||||||
# over-write a design in GDS if one has and the other doesn't
|
# over-write a design in GDS if one has and the other doesn't
|
||||||
# have poly connected, for example.
|
# have poly connected, for example.
|
||||||
name = "{0}_m{1}_w{2:.3f}".format(tx_type, mults, width)
|
name = "{0}_m{1}_w{2:.3f}".format(tx_type, mults, width)
|
||||||
if not add_source_contact:
|
name += "_s{}".format(add_source_contact)
|
||||||
name += "_ns"
|
name += "_d{}".format(add_drain_contact)
|
||||||
if not add_drain_contact:
|
|
||||||
name += "_nd"
|
|
||||||
if series_devices:
|
if series_devices:
|
||||||
name += "_sd"
|
name += "_sd"
|
||||||
if connect_active:
|
if connect_drain_active:
|
||||||
name += "_a"
|
name += "_da"
|
||||||
|
if connect_source_active:
|
||||||
|
name += "_sa"
|
||||||
if connect_poly:
|
if connect_poly:
|
||||||
name += "_p"
|
name += "_p"
|
||||||
if num_contacts:
|
if num_contacts:
|
||||||
|
|
@ -61,13 +76,17 @@ class ptx(design.design):
|
||||||
self.tx_type = tx_type
|
self.tx_type = tx_type
|
||||||
self.mults = mults
|
self.mults = mults
|
||||||
self.tx_width = width
|
self.tx_width = width
|
||||||
self.connect_active = connect_active
|
self.connect_drain_active = connect_drain_active
|
||||||
|
self.connect_source_active = connect_source_active
|
||||||
self.connect_poly = connect_poly
|
self.connect_poly = connect_poly
|
||||||
self.add_source_contact = add_source_contact
|
self.add_source_contact = add_source_contact
|
||||||
self.add_drain_contact = add_drain_contact
|
self.add_drain_contact = add_drain_contact
|
||||||
self.series_devices = series_devices
|
self.series_devices = series_devices
|
||||||
self.num_contacts = num_contacts
|
self.num_contacts = num_contacts
|
||||||
|
|
||||||
|
self.route_layer_width = drc("minwidth_{}".format(self.route_layer))
|
||||||
|
self.route_layer_space = drc("{0}_to_{0}".format(self.route_layer))
|
||||||
|
|
||||||
# Since it has variable height, it is not a pgate.
|
# Since it has variable height, it is not a pgate.
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
# We must always create ptx layout for pbitcell
|
# We must always create ptx layout for pbitcell
|
||||||
|
|
@ -109,6 +128,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 == "s8":
|
if OPTS.tech_name == "s8":
|
||||||
# s8 technology is in microns
|
# s8 technology is in microns
|
||||||
|
(self.tx_width, self.mults) = pgate.bin_width(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,
|
||||||
|
|
@ -265,54 +285,41 @@ class ptx(design.design):
|
||||||
width=poly_width,
|
width=poly_width,
|
||||||
height=self.poly_width)
|
height=self.poly_width)
|
||||||
|
|
||||||
def connect_fingered_active(self, drain_positions, source_positions):
|
def connect_fingered_active(self, positions, pin_name, top):
|
||||||
"""
|
"""
|
||||||
Connect each contact up/down to a source or drain pin
|
Connect each contact up/down to a source or drain pin
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if len(positions) <= 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
layer_space = getattr(self, "{}_space".format(self.route_layer))
|
||||||
|
layer_width = getattr(self, "{}_width".format(self.route_layer))
|
||||||
|
|
||||||
# This is the distance that we must route up or down from the center
|
# This is the distance that we must route up or down from the center
|
||||||
# of the contacts to avoid DRC violations to the other contacts
|
# of the contacts to avoid DRC violations to the other contacts
|
||||||
pin_offset = vector(0,
|
pin_offset = vector(0,
|
||||||
0.5 * self.active_contact.second_layer_height + self.m1_space + 0.5 * self.m1_width)
|
0.5 * self.active_contact.second_layer_height + layer_space + 0.5 * layer_width)
|
||||||
# This is the width of a m1 extend the ends of the pin
|
# This is the width of a m1 extend the ends of the pin
|
||||||
end_offset = vector(self.m1_width / 2.0, 0)
|
end_offset = vector(layer_width / 2.0, 0)
|
||||||
|
|
||||||
# drains always go to the MIDDLE of the cell,
|
# We move the opposite direction from the bottom
|
||||||
# so top of NMOS, bottom of PMOS
|
if not top:
|
||||||
# so reverse the directions for NMOS compared to PMOS.
|
offset = pin_offset.scale(-1, -1)
|
||||||
if self.tx_type == "pmos":
|
|
||||||
drain_dir = -1
|
|
||||||
source_dir = 1
|
|
||||||
else:
|
else:
|
||||||
drain_dir = 1
|
offset = pin_offset
|
||||||
source_dir = -1
|
|
||||||
|
|
||||||
if len(source_positions) > 1:
|
# remove the individual connections
|
||||||
source_offset = pin_offset.scale(source_dir, source_dir)
|
self.remove_layout_pin(pin_name)
|
||||||
# remove the individual connections
|
# Add each vertical segment
|
||||||
self.remove_layout_pin("S")
|
for a in positions:
|
||||||
# Add each vertical segment
|
self.add_path(self.route_layer,
|
||||||
for a in source_positions:
|
[a, a + offset])
|
||||||
self.add_path(("m1"),
|
# Add a single horizontal pin
|
||||||
[a, a + pin_offset.scale(source_dir,
|
self.add_layout_pin_segment_center(text=pin_name,
|
||||||
source_dir)])
|
layer=self.route_layer,
|
||||||
# Add a single horizontal pin
|
start=positions[0] + offset - end_offset,
|
||||||
self.add_layout_pin_segment_center(text="S",
|
end=positions[-1] + offset + end_offset)
|
||||||
layer="m1",
|
|
||||||
start=source_positions[0] + source_offset - end_offset,
|
|
||||||
end=source_positions[-1] + source_offset + end_offset)
|
|
||||||
|
|
||||||
if len(drain_positions)>1:
|
|
||||||
drain_offset = pin_offset.scale(drain_dir,drain_dir)
|
|
||||||
self.remove_layout_pin("D") # remove the individual connections
|
|
||||||
# Add each vertical segment
|
|
||||||
for a in drain_positions:
|
|
||||||
self.add_path(("m1"), [a,a+drain_offset])
|
|
||||||
# Add a single horizontal pin
|
|
||||||
self.add_layout_pin_segment_center(text="D",
|
|
||||||
layer="m1",
|
|
||||||
start=drain_positions[0] + drain_offset - end_offset,
|
|
||||||
end=drain_positions[-1] + drain_offset + end_offset)
|
|
||||||
|
|
||||||
def add_poly(self):
|
def add_poly(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -432,12 +439,12 @@ class ptx(design.design):
|
||||||
label = "S"
|
label = "S"
|
||||||
source_positions.append(pos)
|
source_positions.append(pos)
|
||||||
|
|
||||||
if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact):
|
if (label=="S" and self.add_source_contact):
|
||||||
contact = self.add_diff_contact(label, pos)
|
contact = self.add_diff_contact(label, pos)
|
||||||
if label == "S":
|
self.source_contacts.append(contact)
|
||||||
self.source_contacts.append(contact)
|
elif (label=="D" and self.add_drain_contact):
|
||||||
else:
|
contact = self.add_diff_contact(label, pos)
|
||||||
self.drain_contacts.append(contact)
|
self.drain_contacts.append(contact)
|
||||||
else:
|
else:
|
||||||
self.add_layout_pin_rect_center(text=label,
|
self.add_layout_pin_rect_center(text=label,
|
||||||
layer="active",
|
layer="active",
|
||||||
|
|
@ -453,19 +460,22 @@ class ptx(design.design):
|
||||||
label = "S"
|
label = "S"
|
||||||
source_positions.append(pos)
|
source_positions.append(pos)
|
||||||
|
|
||||||
if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact):
|
if (label=="S" and self.add_source_contact):
|
||||||
contact = self.add_diff_contact(label, pos)
|
contact = self.add_diff_contact(label, pos)
|
||||||
if label == "S":
|
self.source_contacts.append(contact)
|
||||||
self.source_contacts.append(contact)
|
elif (label=="D" and self.add_drain_contact):
|
||||||
else:
|
contact = self.add_diff_contact(label, pos)
|
||||||
self.drain_contacts.append(contact)
|
self.drain_contacts.append(contact)
|
||||||
else:
|
else:
|
||||||
self.add_layout_pin_rect_center(text=label,
|
self.add_layout_pin_rect_center(text=label,
|
||||||
layer="active",
|
layer="active",
|
||||||
offset=pos)
|
offset=pos)
|
||||||
|
|
||||||
if self.connect_active:
|
if self.connect_source_active:
|
||||||
self.connect_fingered_active(drain_positions, source_positions)
|
self.connect_fingered_active(source_positions, "S", top=(self.tx_type=="pmos"))
|
||||||
|
|
||||||
|
if self.connect_drain_active:
|
||||||
|
self.connect_fingered_active(drain_positions, "D", top=(self.tx_type=="nmos"))
|
||||||
|
|
||||||
def get_stage_effort(self, cout):
|
def get_stage_effort(self, cout):
|
||||||
"""Returns an object representing the parameters for delay in tau units."""
|
"""Returns an object representing the parameters for delay in tau units."""
|
||||||
|
|
@ -488,34 +498,39 @@ class ptx(design.design):
|
||||||
return self.mults * self.tx_width / drc("minwidth_tx")
|
return self.mults * self.tx_width / drc("minwidth_tx")
|
||||||
|
|
||||||
def add_diff_contact(self, label, pos):
|
def add_diff_contact(self, label, pos):
|
||||||
contact=self.add_via_center(layers=self.active_stack,
|
|
||||||
offset=pos,
|
|
||||||
size=(1, self.num_contacts),
|
|
||||||
directions=("V", "V"),
|
|
||||||
implant_type=self.implant_type,
|
|
||||||
well_type=self.well_type)
|
|
||||||
|
|
||||||
if hasattr(self, "li_stack"):
|
if label == "S":
|
||||||
contact=self.add_via_center(layers=self.li_stack,
|
layer = self.add_source_contact
|
||||||
offset=pos,
|
elif label == "D":
|
||||||
directions=("V", "V"))
|
layer = self.add_drain_contact
|
||||||
|
else:
|
||||||
|
debug.error("Invalid source drain name.")
|
||||||
|
|
||||||
# contact_area = contact.mod.second_layer_width * contact.mod.second_layer_height
|
if layer != "active":
|
||||||
# min_area = drc("minarea_m1")
|
via=self.add_via_stack_center(offset=pos,
|
||||||
# width = contact.mod.second_layer_width
|
from_layer="active",
|
||||||
# if contact_area < min_area:
|
to_layer=layer,
|
||||||
# height = min_area / width
|
size=(1, self.num_contacts),
|
||||||
# else:
|
directions=("V", "V"),
|
||||||
# height = contact.mod.second_layer_height
|
implant_type=self.implant_type,
|
||||||
width = contact.mod.second_layer_width
|
well_type=self.well_type)
|
||||||
height = contact.mod.second_layer_height
|
|
||||||
|
pin_height = via.mod.second_layer_height
|
||||||
|
pin_width = via.mod.second_layer_width
|
||||||
|
else:
|
||||||
|
via = None
|
||||||
|
|
||||||
|
pin_height = None
|
||||||
|
pin_width = None
|
||||||
|
|
||||||
|
# Source drain vias are all vertical
|
||||||
self.add_layout_pin_rect_center(text=label,
|
self.add_layout_pin_rect_center(text=label,
|
||||||
layer="m1",
|
layer=layer,
|
||||||
offset=pos,
|
offset=pos,
|
||||||
width=width,
|
width=pin_width,
|
||||||
height=height)
|
height=pin_height)
|
||||||
|
|
||||||
return(contact)
|
return(via)
|
||||||
|
|
||||||
def get_cin(self):
|
def get_cin(self):
|
||||||
"""Returns the relative gate cin of the tx"""
|
"""Returns the relative gate cin of the tx"""
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,13 @@ class router_tech:
|
||||||
"""
|
"""
|
||||||
def __init__(self, layers, rail_track_width):
|
def __init__(self, layers, rail_track_width):
|
||||||
"""
|
"""
|
||||||
Allows us to change the layers that we are routing on. First layer
|
Allows us to change the layers that we are routing on.
|
||||||
is always horizontal, middle is via, and last is always
|
This uses the preferreed directions.
|
||||||
vertical.
|
|
||||||
"""
|
"""
|
||||||
self.layers = layers
|
self.layers = layers
|
||||||
self.rail_track_width = rail_track_width
|
self.rail_track_width = rail_track_width
|
||||||
|
|
||||||
if len(self.layers) == 1:
|
if len(self.layers) == 1:
|
||||||
if preferred_directions[self.layers[0]] != "H":
|
|
||||||
debug.warning("Using '{}' for horizontal routing, but it " \
|
|
||||||
"prefers vertical routing".format(self.layers[0]))
|
|
||||||
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
|
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
|
||||||
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
|
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
|
||||||
|
|
||||||
|
|
@ -42,13 +38,21 @@ class router_tech:
|
||||||
|
|
||||||
# figure out wich of the two layers prefers horizontal/vertical
|
# figure out wich of the two layers prefers horizontal/vertical
|
||||||
# routing
|
# routing
|
||||||
if preferred_directions[try_horiz_layer] == "H" and preferred_directions[try_vert_layer] == "V":
|
self.horiz_layer_name = None
|
||||||
|
self.vert_layer_name = None
|
||||||
|
|
||||||
|
if preferred_directions[try_horiz_layer] == "H":
|
||||||
self.horiz_layer_name = try_horiz_layer
|
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
|
self.vert_layer_name = try_vert_layer
|
||||||
else:
|
else:
|
||||||
raise ValueError("Layer '{}' and '{}' are using the wrong " \
|
self.vert_layer_name = try_horiz_layer
|
||||||
"preferred_directions '{}' and '{}'. Only "\
|
|
||||||
"('H', 'V') are supported")
|
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))
|
via_connect = contact(self.layers, (1, 1))
|
||||||
max_via_size = max(via_connect.width,via_connect.height)
|
max_via_size = max(via_connect.width,via_connect.height)
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,15 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import gdsMill
|
|
||||||
import tech
|
|
||||||
import math
|
|
||||||
import debug
|
import debug
|
||||||
from globals import OPTS,print_time
|
from globals import print_time
|
||||||
from contact import contact
|
|
||||||
from pin_group import pin_group
|
|
||||||
from pin_layout import pin_layout
|
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
from router import router
|
from router import router
|
||||||
from direction import direction
|
from direction import direction
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import grid
|
|
||||||
import grid_utils
|
import grid_utils
|
||||||
|
|
||||||
|
|
||||||
class supply_grid_router(router):
|
class supply_grid_router(router):
|
||||||
"""
|
"""
|
||||||
A router class to read an obstruction map from a gds and
|
A router class to read an obstruction map from a gds and
|
||||||
|
|
@ -45,13 +39,12 @@ class supply_grid_router(router):
|
||||||
|
|
||||||
print_time("Init supply router", datetime.now(), start_time, 3)
|
print_time("Init supply router", datetime.now(), start_time, 3)
|
||||||
|
|
||||||
|
|
||||||
def create_routing_grid(self):
|
def create_routing_grid(self):
|
||||||
"""
|
"""
|
||||||
Create a sprase routing grid with A* expansion functions.
|
Create a sprase routing grid with A* expansion functions.
|
||||||
"""
|
"""
|
||||||
size = self.ur - self.ll
|
size = self.ur - self.ll
|
||||||
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
|
debug.info(1, "Size: {0} x {1}".format(size.x, size.y))
|
||||||
|
|
||||||
import supply_grid
|
import supply_grid
|
||||||
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
|
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
|
||||||
|
|
@ -60,12 +53,12 @@ class supply_grid_router(router):
|
||||||
"""
|
"""
|
||||||
Add power supply rails and connect all pins to these rails.
|
Add power supply rails and connect all pins to these rails.
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
|
debug.info(1, "Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
|
||||||
self.vdd_name = vdd_name
|
self.vdd_name = vdd_name
|
||||||
self.gnd_name = gnd_name
|
self.gnd_name = gnd_name
|
||||||
|
|
||||||
# Clear the pins if we have previously routed
|
# Clear the pins if we have previously routed
|
||||||
if (hasattr(self,'rg')):
|
if (hasattr(self, 'rg')):
|
||||||
self.clear_pins()
|
self.clear_pins()
|
||||||
else:
|
else:
|
||||||
# Creat a routing grid over the entire area
|
# Creat a routing grid over the entire area
|
||||||
|
|
@ -76,32 +69,32 @@ class supply_grid_router(router):
|
||||||
# Get the pin shapes
|
# Get the pin shapes
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
|
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
|
||||||
print_time("Finding pins and blockages",datetime.now(), start_time, 3)
|
print_time("Finding pins and blockages", datetime.now(), start_time, 3)
|
||||||
# Add the supply rails in a mesh network and connect H/V with vias
|
# Add the supply rails in a mesh network and connect H/V with vias
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages(self.gnd_name)
|
self.prepare_blockages(self.gnd_name)
|
||||||
# Determine the rail locations
|
# Determine the rail locations
|
||||||
self.route_supply_rails(self.gnd_name,0)
|
self.route_supply_rails(self.gnd_name, 0)
|
||||||
|
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages(self.vdd_name)
|
self.prepare_blockages(self.vdd_name)
|
||||||
# Determine the rail locations
|
# Determine the rail locations
|
||||||
self.route_supply_rails(self.vdd_name,1)
|
self.route_supply_rails(self.vdd_name, 1)
|
||||||
print_time("Routing supply rails",datetime.now(), start_time, 3)
|
print_time("Routing supply rails", datetime.now(), start_time, 3)
|
||||||
|
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
self.route_simple_overlaps(vdd_name)
|
self.route_simple_overlaps(vdd_name)
|
||||||
self.route_simple_overlaps(gnd_name)
|
self.route_simple_overlaps(gnd_name)
|
||||||
print_time("Simple overlap routing",datetime.now(), start_time, 3)
|
print_time("Simple overlap routing", datetime.now(), start_time, 3)
|
||||||
|
|
||||||
# Route the supply pins to the supply rails
|
# Route the supply pins to the supply rails
|
||||||
# Route vdd first since we want it to be shorter
|
# Route vdd first since we want it to be shorter
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
self.route_pins_to_rails(vdd_name)
|
self.route_pins_to_rails(vdd_name)
|
||||||
self.route_pins_to_rails(gnd_name)
|
self.route_pins_to_rails(gnd_name)
|
||||||
print_time("Maze routing supplies",datetime.now(), start_time, 3)
|
print_time("Maze routing supplies", datetime.now(), start_time, 3)
|
||||||
#self.write_debug_gds("final.gds",False)
|
# self.write_debug_gds("final.gds", False)
|
||||||
|
|
||||||
# Did we route everything??
|
# Did we route everything??
|
||||||
if not self.check_all_routed(vdd_name):
|
if not self.check_all_routed(vdd_name):
|
||||||
|
|
@ -111,7 +104,6 @@ class supply_grid_router(router):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def check_all_routed(self, pin_name):
|
def check_all_routed(self, pin_name):
|
||||||
"""
|
"""
|
||||||
Check that all pin groups are routed.
|
Check that all pin groups are routed.
|
||||||
|
|
@ -125,7 +117,7 @@ class supply_grid_router(router):
|
||||||
This checks for simple cases where a pin component already overlaps a supply rail.
|
This checks for simple cases where a pin component already overlaps a supply rail.
|
||||||
It will add an enclosure to ensure the overlap in wide DRC rule cases.
|
It will add an enclosure to ensure the overlap in wide DRC rule cases.
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Routing simple overlap pins for {0}".format(pin_name))
|
debug.info(1, "Routing simple overlap pins for {0}".format(pin_name))
|
||||||
|
|
||||||
# These are the wire tracks
|
# These are the wire tracks
|
||||||
wire_tracks = self.supply_rail_tracks[pin_name]
|
wire_tracks = self.supply_rail_tracks[pin_name]
|
||||||
|
|
@ -142,10 +134,10 @@ class supply_grid_router(router):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Else, if we overlap some of the space track, we can patch it with an enclosure
|
# Else, if we overlap some of the space track, we can patch it with an enclosure
|
||||||
#pg.create_simple_overlap_enclosure(pg.grids)
|
# pg.create_simple_overlap_enclosure(pg.grids)
|
||||||
#pg.add_enclosure(self.cell)
|
# pg.add_enclosure(self.cell)
|
||||||
|
|
||||||
debug.info(1,"Routed {} simple overlap pins".format(routed_count))
|
debug.info(1, "Routed {} simple overlap pins".format(routed_count))
|
||||||
|
|
||||||
def finalize_supply_rails(self, name):
|
def finalize_supply_rails(self, name):
|
||||||
"""
|
"""
|
||||||
|
|
@ -158,7 +150,7 @@ class supply_grid_router(router):
|
||||||
|
|
||||||
connections = set()
|
connections = set()
|
||||||
via_areas = []
|
via_areas = []
|
||||||
for i1,r1 in enumerate(all_rails):
|
for i1, r1 in enumerate(all_rails):
|
||||||
# Only consider r1 horizontal rails
|
# Only consider r1 horizontal rails
|
||||||
e = next(iter(r1))
|
e = next(iter(r1))
|
||||||
if e.z==1:
|
if e.z==1:
|
||||||
|
|
@ -166,9 +158,9 @@ class supply_grid_router(router):
|
||||||
|
|
||||||
# We need to move this rail to the other layer for the z indices to match
|
# We need to move this rail to the other layer for the z indices to match
|
||||||
# during the intersection. This also makes a copy.
|
# during the intersection. This also makes a copy.
|
||||||
new_r1 = {vector3d(i.x,i.y,1) for i in r1}
|
new_r1 = {vector3d(i.x, i.y, 1) for i in r1}
|
||||||
|
|
||||||
for i2,r2 in enumerate(all_rails):
|
for i2, r2 in enumerate(all_rails):
|
||||||
# Never compare to yourself
|
# Never compare to yourself
|
||||||
if i1==i2:
|
if i1==i2:
|
||||||
continue
|
continue
|
||||||
|
|
@ -184,16 +176,16 @@ class supply_grid_router(router):
|
||||||
# the overlap area for placement of a via
|
# the overlap area for placement of a via
|
||||||
overlap = new_r1 & r2
|
overlap = new_r1 & r2
|
||||||
if len(overlap) >= 1:
|
if len(overlap) >= 1:
|
||||||
debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap))
|
debug.info(3, "Via overlap {0} {1}".format(len(overlap),overlap))
|
||||||
connections.update([i1,i2])
|
connections.update([i1, i2])
|
||||||
via_areas.append(overlap)
|
via_areas.append(overlap)
|
||||||
|
|
||||||
# Go through and add the vias at the center of the intersection
|
# Go through and add the vias at the center of the intersection
|
||||||
for area in via_areas:
|
for area in via_areas:
|
||||||
ll = grid_utils.get_lower_left(area)
|
ll = grid_utils.get_lower_left(area)
|
||||||
ur = grid_utils.get_upper_right(area)
|
ur = grid_utils.get_upper_right(area)
|
||||||
center = (ll + ur).scale(0.5,0.5,0)
|
center = (ll + ur).scale(0.5, 0.5, 0)
|
||||||
self.add_via(center,1)
|
self.add_via(center, 1)
|
||||||
|
|
||||||
# Determien which indices were not connected to anything above
|
# Determien which indices were not connected to anything above
|
||||||
missing_indices = set([x for x in range(len(self.supply_rails[name]))])
|
missing_indices = set([x for x in range(len(self.supply_rails[name]))])
|
||||||
|
|
@ -204,14 +196,13 @@ class supply_grid_router(router):
|
||||||
for rail_index in sorted(missing_indices, reverse=True):
|
for rail_index in sorted(missing_indices, reverse=True):
|
||||||
ll = grid_utils.get_lower_left(all_rails[rail_index])
|
ll = grid_utils.get_lower_left(all_rails[rail_index])
|
||||||
ur = grid_utils.get_upper_right(all_rails[rail_index])
|
ur = grid_utils.get_upper_right(all_rails[rail_index])
|
||||||
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur))
|
debug.info(1, "Removing disconnected supply rail {0} .. {1}".format(ll, ur))
|
||||||
self.supply_rails[name].pop(rail_index)
|
self.supply_rails[name].pop(rail_index)
|
||||||
|
|
||||||
# Make the supply rails into a big giant set of grids for easy blockages.
|
# Make the supply rails into a big giant set of grids for easy blockages.
|
||||||
# Must be done after we determine which ones are connected.
|
# Must be done after we determine which ones are connected.
|
||||||
self.create_supply_track_set(name)
|
self.create_supply_track_set(name)
|
||||||
|
|
||||||
|
|
||||||
def add_supply_rails(self, name):
|
def add_supply_rails(self, name):
|
||||||
"""
|
"""
|
||||||
Add the shapes that represent the routed supply rails.
|
Add the shapes that represent the routed supply rails.
|
||||||
|
|
@ -223,7 +214,7 @@ class supply_grid_router(router):
|
||||||
ur = grid_utils.get_upper_right(rail)
|
ur = grid_utils.get_upper_right(rail)
|
||||||
z = ll.z
|
z = ll.z
|
||||||
pin = self.compute_pin_enclosure(ll, ur, z, name)
|
pin = self.compute_pin_enclosure(ll, ur, z, name)
|
||||||
debug.info(3,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin))
|
debug.info(3, "Adding supply rail {0} {1}->{2} {3}".format(name, ll, ur, pin))
|
||||||
self.cell.add_layout_pin(text=name,
|
self.cell.add_layout_pin(text=name,
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
offset=pin.ll(),
|
offset=pin.ll(),
|
||||||
|
|
@ -244,18 +235,17 @@ class supply_grid_router(router):
|
||||||
min_yoffset = self.rg.ll.y
|
min_yoffset = self.rg.ll.y
|
||||||
min_xoffset = self.rg.ll.x
|
min_xoffset = self.rg.ll.x
|
||||||
|
|
||||||
|
|
||||||
# Horizontal supply rails
|
# Horizontal supply rails
|
||||||
start_offset = min_yoffset + supply_number
|
start_offset = min_yoffset + supply_number
|
||||||
for offset in range(start_offset, max_yoffset, 2):
|
for offset in range(start_offset, max_yoffset, 2):
|
||||||
# Seed the function at the location with the given width
|
# Seed the function at the location with the given width
|
||||||
wave = [vector3d(min_xoffset,offset,0)]
|
wave = [vector3d(min_xoffset, offset, 0)]
|
||||||
# While we can keep expanding east in this horizontal track
|
# While we can keep expanding east in this horizontal track
|
||||||
while wave and wave[0].x < max_xoffset:
|
while wave and wave[0].x < max_xoffset:
|
||||||
added_rail = self.find_supply_rail(name, wave, direction.EAST)
|
added_rail = self.find_supply_rail(name, wave, direction.EAST)
|
||||||
if not added_rail:
|
if not added_rail:
|
||||||
# Just seed with the next one
|
# Just seed with the next one
|
||||||
wave = [x+vector3d(1,0,0) for x in wave]
|
wave = [x+vector3d(1, 0, 0) for x in wave]
|
||||||
else:
|
else:
|
||||||
# Seed with the neighbor of the end of the last rail
|
# Seed with the neighbor of the end of the last rail
|
||||||
wave = added_rail.neighbor(direction.EAST)
|
wave = added_rail.neighbor(direction.EAST)
|
||||||
|
|
@ -264,13 +254,13 @@ class supply_grid_router(router):
|
||||||
start_offset = min_xoffset + supply_number
|
start_offset = min_xoffset + supply_number
|
||||||
for offset in range(start_offset, max_xoffset, 2):
|
for offset in range(start_offset, max_xoffset, 2):
|
||||||
# Seed the function at the location with the given width
|
# Seed the function at the location with the given width
|
||||||
wave = [vector3d(offset,min_yoffset,1)]
|
wave = [vector3d(offset, min_yoffset, 1)]
|
||||||
# While we can keep expanding north in this vertical track
|
# While we can keep expanding north in this vertical track
|
||||||
while wave and wave[0].y < max_yoffset:
|
while wave and wave[0].y < max_yoffset:
|
||||||
added_rail = self.find_supply_rail(name, wave, direction.NORTH)
|
added_rail = self.find_supply_rail(name, wave, direction.NORTH)
|
||||||
if not added_rail:
|
if not added_rail:
|
||||||
# Just seed with the next one
|
# Just seed with the next one
|
||||||
wave = [x+vector3d(0,1,0) for x in wave]
|
wave = [x + vector3d(0, 1, 0) for x in wave]
|
||||||
else:
|
else:
|
||||||
# Seed with the neighbor of the end of the last rail
|
# Seed with the neighbor of the end of the last rail
|
||||||
wave = added_rail.neighbor(direction.NORTH)
|
wave = added_rail.neighbor(direction.NORTH)
|
||||||
|
|
@ -295,7 +285,6 @@ class supply_grid_router(router):
|
||||||
# as it will be used to find the next start location
|
# as it will be used to find the next start location
|
||||||
return wave_path
|
return wave_path
|
||||||
|
|
||||||
|
|
||||||
def probe_supply_rail(self, name, start_wave, direct):
|
def probe_supply_rail(self, name, start_wave, direct):
|
||||||
"""
|
"""
|
||||||
This finds the first valid starting location and routes a supply rail
|
This finds the first valid starting location and routes a supply rail
|
||||||
|
|
@ -328,23 +317,19 @@ class supply_grid_router(router):
|
||||||
data structure. Return whether it was added or not.
|
data structure. Return whether it was added or not.
|
||||||
"""
|
"""
|
||||||
# We must have at least 2 tracks to drop plus 2 tracks for a via
|
# We must have at least 2 tracks to drop plus 2 tracks for a via
|
||||||
if len(wave_path)>=4*self.rail_track_width:
|
if len(wave_path) >= 4 * self.rail_track_width:
|
||||||
grid_set = wave_path.get_grids()
|
grid_set = wave_path.get_grids()
|
||||||
self.supply_rails[name].append(grid_set)
|
self.supply_rails[name].append(grid_set)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def route_supply_rails(self, name, supply_number):
|
def route_supply_rails(self, name, supply_number):
|
||||||
"""
|
"""
|
||||||
Route the horizontal and vertical supply rails across the entire design.
|
Route the horizontal and vertical supply rails across the entire design.
|
||||||
Must be done with lower left at 0,0
|
Must be done with lower left at 0,0
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Routing supply rail {0}.".format(name))
|
debug.info(1, "Routing supply rail {0}.".format(name))
|
||||||
|
|
||||||
# Compute the grid locations of the supply rails
|
# Compute the grid locations of the supply rails
|
||||||
self.compute_supply_rails(name, supply_number)
|
self.compute_supply_rails(name, supply_number)
|
||||||
|
|
@ -355,7 +340,6 @@ class supply_grid_router(router):
|
||||||
# Add the rails themselves
|
# Add the rails themselves
|
||||||
self.add_supply_rails(name)
|
self.add_supply_rails(name)
|
||||||
|
|
||||||
|
|
||||||
def create_supply_track_set(self, pin_name):
|
def create_supply_track_set(self, pin_name):
|
||||||
"""
|
"""
|
||||||
Make a single set of all the tracks for the rail and wire itself.
|
Make a single set of all the tracks for the rail and wire itself.
|
||||||
|
|
@ -365,8 +349,6 @@ class supply_grid_router(router):
|
||||||
rail_set.update(rail)
|
rail_set.update(rail)
|
||||||
self.supply_rail_tracks[pin_name] = rail_set
|
self.supply_rail_tracks[pin_name] = rail_set
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def route_pins_to_rails(self, pin_name):
|
def route_pins_to_rails(self, pin_name):
|
||||||
"""
|
"""
|
||||||
This will route each of the remaining pin components to the supply rails.
|
This will route each of the remaining pin components to the supply rails.
|
||||||
|
|
@ -374,14 +356,14 @@ class supply_grid_router(router):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
|
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
|
||||||
debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name,
|
debug.info(1, "Maze routing {0} with {1} pin components to connect.".format(pin_name,
|
||||||
remaining_components))
|
remaining_components))
|
||||||
|
|
||||||
for index,pg in enumerate(self.pin_groups[pin_name]):
|
for index, pg in enumerate(self.pin_groups[pin_name]):
|
||||||
if pg.is_routed():
|
if pg.is_routed():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
debug.info(3,"Routing component {0} {1}".format(pin_name, index))
|
debug.info(3, "Routing component {0} {1}".format(pin_name, index))
|
||||||
|
|
||||||
# Clear everything in the routing grid.
|
# Clear everything in the routing grid.
|
||||||
self.rg.reinit()
|
self.rg.reinit()
|
||||||
|
|
@ -400,28 +382,26 @@ class supply_grid_router(router):
|
||||||
|
|
||||||
# Actually run the A* router
|
# Actually run the A* router
|
||||||
if not self.run_router(detour_scale=5):
|
if not self.run_router(detour_scale=5):
|
||||||
self.write_debug_gds("debug_route.gds",False)
|
self.write_debug_gds("debug_route.gds", False)
|
||||||
|
|
||||||
#if index==3 and pin_name=="vdd":
|
|
||||||
# self.write_debug_gds("route.gds",False)
|
|
||||||
|
|
||||||
|
# if index==3 and pin_name=="vdd":
|
||||||
|
# self.write_debug_gds("route.gds",False)
|
||||||
|
|
||||||
def add_supply_rail_target(self, pin_name):
|
def add_supply_rail_target(self, pin_name):
|
||||||
"""
|
"""
|
||||||
Add the supply rails of given name as a routing target.
|
Add the supply rails of given name as a routing target.
|
||||||
"""
|
"""
|
||||||
debug.info(4,"Add supply rail target {}".format(pin_name))
|
debug.info(4, "Add supply rail target {}".format(pin_name))
|
||||||
# Add the wire itself as the target
|
# Add the wire itself as the target
|
||||||
self.rg.set_target(self.supply_rail_tracks[pin_name])
|
self.rg.set_target(self.supply_rail_tracks[pin_name])
|
||||||
# But unblock all the rail tracks including the space
|
# But unblock all the rail tracks including the space
|
||||||
self.rg.set_blocked(self.supply_rail_tracks[pin_name],False)
|
self.rg.set_blocked(self.supply_rail_tracks[pin_name], False)
|
||||||
|
|
||||||
|
|
||||||
def set_supply_rail_blocked(self, value=True):
|
def set_supply_rail_blocked(self, value=True):
|
||||||
"""
|
"""
|
||||||
Add the supply rails of given name as a routing target.
|
Add the supply rails of given name as a routing target.
|
||||||
"""
|
"""
|
||||||
debug.info(4,"Blocking supply rail")
|
debug.info(4, "Blocking supply rail")
|
||||||
for rail_name in self.supply_rail_tracks:
|
for rail_name in self.supply_rail_tracks:
|
||||||
self.rg.set_blocked(self.supply_rail_tracks[rail_name])
|
self.rg.set_blocked(self.supply_rail_tracks[rail_name])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,22 @@ class sram_1bank(sram_base):
|
||||||
wmask_pos = [None] * len(self.all_ports)
|
wmask_pos = [None] * len(self.all_ports)
|
||||||
data_pos = [None] * len(self.all_ports)
|
data_pos = [None] * len(self.all_ports)
|
||||||
|
|
||||||
|
# These positions utilize the channel route sizes.
|
||||||
|
# FIXME: Auto-compute these rather than manual computation.
|
||||||
|
# If a horizontal channel, they rely on the vertical channel non-preferred (contacted) pitch.
|
||||||
|
# If a vertical channel, they rely on the horizontal channel non-preferred (contacted) pitch.
|
||||||
|
# So, m3 non-pref pitch means that this is routed on the m2 layer.
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
max_gap_size = self.m3_pitch * self.word_size + 2 * self.m1_pitch
|
self.data_bus_gap = self.m4_nonpref_pitch * 2
|
||||||
max_gap_size_wmask = self.m2_pitch * max(self.num_wmasks + 1, self.col_addr_size + 1) + 2 * self.m1_pitch
|
self.data_bus_size = self.m4_nonpref_pitch * (self.word_size) + self.data_bus_gap
|
||||||
|
self.wmask_bus_gap = self.m2_nonpref_pitch * 2
|
||||||
|
self.wmask_bus_size = self.m2_nonpref_pitch * (max(self.num_wmasks + 1, self.col_addr_size + 1)) + self.wmask_bus_gap
|
||||||
else:
|
else:
|
||||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
self.data_bus_gap = self.m3_nonpref_pitch * 2
|
||||||
# The M1 pitch is for supply rail spacings
|
self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + 1, self.col_addr_size + 1)) + self.data_bus_gap
|
||||||
max_gap_size = self.m2_pitch * max(self.word_size + 1,self.col_addr_size + 1) + 2 * self.m1_pitch
|
|
||||||
|
self.col_addr_bus_gap = self.m2_nonpref_pitch * 2
|
||||||
|
self.col_addr_bus_size = self.m2_nonpref_pitch * (self.col_addr_size) + self.col_addr_bus_gap
|
||||||
|
|
||||||
# Port 0
|
# Port 0
|
||||||
port = 0
|
port = 0
|
||||||
|
|
@ -74,12 +83,12 @@ class sram_1bank(sram_base):
|
||||||
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(self.bank.bank_array_ll.x,
|
wmask_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||||
-max_gap_size_wmask - self.dff.height)
|
- self.wmask_bus_size - self.dff.height)
|
||||||
self.wmask_dff_insts[port].place(wmask_pos[port])
|
self.wmask_dff_insts[port].place(wmask_pos[port])
|
||||||
|
|
||||||
# Add the data flops below the write mask flops.
|
# Add the data flops below the write mask flops.
|
||||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||||
-max_gap_size - max_gap_size_wmask - 2 * self.dff.height)
|
- self.data_bus_size - self.wmask_bus_size - 2 * self.dff.height)
|
||||||
self.data_dff_insts[port].place(data_pos[port])
|
self.data_dff_insts[port].place(data_pos[port])
|
||||||
else:
|
else:
|
||||||
# Add the data flops below the bank to the right of the lower-left of bank array
|
# Add the data flops below the bank to the right of the lower-left of bank array
|
||||||
|
|
@ -89,7 +98,7 @@ class sram_1bank(sram_base):
|
||||||
# sense amps.
|
# sense amps.
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||||
-max_gap_size - self.dff.height)
|
-self.data_bus_size - self.dff.height)
|
||||||
self.data_dff_insts[port].place(data_pos[port])
|
self.data_dff_insts[port].place(data_pos[port])
|
||||||
else:
|
else:
|
||||||
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||||
|
|
@ -99,10 +108,10 @@ class sram_1bank(sram_base):
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
|
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
|
||||||
-max_gap_size_wmask - self.col_addr_dff_insts[port].height)
|
-self.wmask_bus_size - self.col_addr_dff_insts[port].height)
|
||||||
else:
|
else:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
|
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
|
||||||
-max_gap_size - self.col_addr_dff_insts[port].height)
|
-self.data_bus_size - self.col_addr_dff_insts[port].height)
|
||||||
self.col_addr_dff_insts[port].place(col_addr_pos[port])
|
self.col_addr_dff_insts[port].place(col_addr_pos[port])
|
||||||
else:
|
else:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
col_addr_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||||
|
|
@ -127,12 +136,12 @@ class sram_1bank(sram_base):
|
||||||
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(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width,
|
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width,
|
||||||
self.bank.height + max_gap_size_wmask + self.dff.height)
|
self.bank.height + self.wmask_bus_size + self.dff.height)
|
||||||
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
|
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
|
||||||
|
|
||||||
# Add the data flops below the write mask flops
|
# Add the data flops below the write mask flops
|
||||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||||
self.bank.height + max_gap_size_wmask + max_gap_size + 2 * self.dff.height)
|
self.bank.height + self.wmask_bus_size + self.data_bus_size + 2 * self.dff.height)
|
||||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||||
else:
|
else:
|
||||||
# Add the data flops above the bank to the left of the upper-right of bank array
|
# Add the data flops above the bank to the left of the upper-right of bank array
|
||||||
|
|
@ -141,17 +150,17 @@ class sram_1bank(sram_base):
|
||||||
# These flops go below the sensing and leave a gap to channel route to the
|
# These flops go below the sensing and leave a gap to channel route to the
|
||||||
# sense amps.
|
# sense amps.
|
||||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||||
self.bank.height + max_gap_size + self.dff.height)
|
self.bank.height + self.data_bus_size + self.dff.height)
|
||||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||||
|
|
||||||
# Add the col address flops above the bank to the right of the upper-right of bank array
|
# Add the col address flops above the bank to the right of the upper-right of bank array
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
|
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
|
||||||
self.bank.height + max_gap_size_wmask + self.dff.height)
|
self.bank.height + self.wmask_bus_size + self.dff.height)
|
||||||
else:
|
else:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
|
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
|
||||||
self.bank.height + max_gap_size + self.dff.height)
|
self.bank.height + self.data_bus_size + self.dff.height)
|
||||||
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
|
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
|
||||||
else:
|
else:
|
||||||
col_addr_pos[port] = self.bank_inst.ur()
|
col_addr_pos[port] = self.bank_inst.ur()
|
||||||
|
|
@ -245,7 +254,7 @@ class sram_1bank(sram_base):
|
||||||
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
|
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
|
||||||
# to route vertically. For port1, it is to the left.
|
# to route vertically. For port1, it is to the left.
|
||||||
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
|
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
|
||||||
if port%2:
|
if port % 2:
|
||||||
control_clk_buf_pos = control_clk_buf_pin.lc()
|
control_clk_buf_pos = control_clk_buf_pin.lc()
|
||||||
row_addr_clk_pos = row_addr_clk_pin.lc()
|
row_addr_clk_pos = row_addr_clk_pin.lc()
|
||||||
mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch,
|
mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch,
|
||||||
|
|
@ -258,19 +267,20 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# This is the steiner point where the net branches out
|
# This is the steiner point where the net branches out
|
||||||
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
|
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
|
||||||
self.add_path("m1", [control_clk_buf_pos, clk_steiner_pos])
|
self.add_path(control_clk_buf_pin.layer, [control_clk_buf_pos, clk_steiner_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_stack_center(from_layer=control_clk_buf_pin.layer,
|
||||||
offset=clk_steiner_pos)
|
to_layer="m2",
|
||||||
|
offset=clk_steiner_pos)
|
||||||
|
|
||||||
# Note, the via to the control logic is taken care of above
|
# Note, the via to the control logic is taken care of above
|
||||||
self.add_wire(("m3", "via2", "m2"),
|
self.add_wire(self.m2_stack[::-1],
|
||||||
[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
|
[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
|
||||||
|
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
|
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
|
||||||
dff_clk_pos = dff_clk_pin.center()
|
dff_clk_pos = dff_clk_pin.center()
|
||||||
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
|
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
|
||||||
self.add_wire(("m3", "via2", "m2"),
|
self.add_wire(self.m2_stack[::-1],
|
||||||
[dff_clk_pos, mid_pos, clk_steiner_pos])
|
[dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||||
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
|
|
@ -282,7 +292,7 @@ class sram_1bank(sram_base):
|
||||||
self.add_path("m2",
|
self.add_path("m2",
|
||||||
[mid_pos, clk_steiner_pos],
|
[mid_pos, clk_steiner_pos],
|
||||||
width=max(m2_via.width, m2_via.height))
|
width=max(m2_via.width, m2_via.height))
|
||||||
self.add_wire(("m3", "via2", "m2"),
|
self.add_wire(self.m2_stack[::-1],
|
||||||
[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||||
|
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
|
|
@ -292,7 +302,7 @@ class sram_1bank(sram_base):
|
||||||
# In some designs, the steiner via will be too close to the mid_pos via
|
# In some designs, the steiner via will be too close to the mid_pos via
|
||||||
# so make the wire as wide as the contacts
|
# so make the wire as wide as the contacts
|
||||||
self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height))
|
self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height))
|
||||||
self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
|
self.add_wire(self.m2_stack[::-1], [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||||
|
|
||||||
def route_control_logic(self):
|
def route_control_logic(self):
|
||||||
""" Route the control logic pins that are not inputs """
|
""" Route the control logic pins that are not inputs """
|
||||||
|
|
@ -304,14 +314,13 @@ class sram_1bank(sram_base):
|
||||||
continue
|
continue
|
||||||
src_pin = self.control_logic_insts[port].get_pin(signal)
|
src_pin = self.control_logic_insts[port].get_pin(signal)
|
||||||
dest_pin = self.bank_inst.get_pin(signal + "{}".format(port))
|
dest_pin = self.bank_inst.get_pin(signal + "{}".format(port))
|
||||||
self.connect_vbus_m2m3(src_pin, dest_pin)
|
self.connect_vbus(src_pin, dest_pin)
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
# Only input (besides pins) is the replica bitline
|
# Only input (besides pins) is the replica bitline
|
||||||
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
|
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
|
||||||
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
||||||
|
self.connect_hbus(src_pin, dest_pin)
|
||||||
self.connect_hbus_m2m3(src_pin, dest_pin)
|
|
||||||
|
|
||||||
def route_row_addr_dff(self):
|
def route_row_addr_dff(self):
|
||||||
""" Connect the output of the row flops to the bank pins """
|
""" Connect the output of the row flops to the bank pins """
|
||||||
|
|
@ -324,33 +333,37 @@ class sram_1bank(sram_base):
|
||||||
flop_pos = flop_pin.center()
|
flop_pos = flop_pin.center()
|
||||||
bank_pos = bank_pin.center()
|
bank_pos = bank_pin.center()
|
||||||
mid_pos = vector(bank_pos.x, flop_pos.y)
|
mid_pos = vector(bank_pos.x, flop_pos.y)
|
||||||
self.add_wire(("m3", "via2", "m2"),
|
self.add_wire(self.m2_stack[::-1],
|
||||||
[flop_pos, mid_pos, bank_pos])
|
[flop_pos, mid_pos, bank_pos])
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_stack_center(from_layer=flop_pin.layer,
|
||||||
offset=flop_pos)
|
to_layer="m3",
|
||||||
|
offset=flop_pos)
|
||||||
|
|
||||||
def route_col_addr_dff(self):
|
def route_col_addr_dff(self):
|
||||||
""" Connect the output of the col flops to the bank pins """
|
""" Connect the output of the col flops to the bank pins """
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
if port%2:
|
if port % 2:
|
||||||
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size + 2) * self.m1_pitch)
|
offset = self.col_addr_dff_insts[port].ll() - vector(0, self.col_addr_bus_size)
|
||||||
else:
|
else:
|
||||||
offset = self.col_addr_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
offset = self.col_addr_dff_insts[port].ul() + vector(0, self.col_addr_bus_gap)
|
||||||
|
|
||||||
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
|
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
|
||||||
col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
|
col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
|
||||||
pitch=self.m1_pitch,
|
|
||||||
offset=offset,
|
offset=offset,
|
||||||
names=bus_names,
|
names=bus_names,
|
||||||
length=self.col_addr_dff_insts[port].width)
|
length=self.col_addr_dff_insts[port].width)
|
||||||
|
|
||||||
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)]
|
||||||
data_dff_map = zip(dff_names, bus_names)
|
data_dff_map = zip(dff_names, bus_names)
|
||||||
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets)
|
self.connect_horizontal_bus(data_dff_map,
|
||||||
|
self.col_addr_dff_insts[port],
|
||||||
|
col_addr_bus_offsets)
|
||||||
|
|
||||||
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
|
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
|
||||||
data_bank_map = zip(bank_names, bus_names)
|
data_bank_map = zip(bank_names, bus_names)
|
||||||
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets)
|
self.connect_horizontal_bus(data_bank_map,
|
||||||
|
self.bank_inst,
|
||||||
|
col_addr_bus_offsets)
|
||||||
|
|
||||||
def route_data_dff(self):
|
def route_data_dff(self):
|
||||||
""" Connect the output of the data flops to the write driver """
|
""" Connect the output of the data flops to the write driver """
|
||||||
|
|
@ -358,48 +371,47 @@ class sram_1bank(sram_base):
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
if port % 2:
|
if port % 2:
|
||||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m3_pitch)
|
offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size)
|
||||||
else:
|
else:
|
||||||
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch)
|
offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap)
|
||||||
else:
|
else:
|
||||||
if port % 2:
|
if port % 2:
|
||||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m1_pitch)
|
offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size)
|
||||||
else:
|
else:
|
||||||
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap)
|
||||||
|
|
||||||
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
||||||
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
|
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
for x in dff_names:
|
for x in dff_names:
|
||||||
pin_offset = self.data_dff_insts[port].get_pin(x).center()
|
pin = self.data_dff_insts[port].get_pin(x)
|
||||||
|
pin_offset = pin.center()
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=pin_offset,
|
offset=pin_offset,
|
||||||
directions=("V", "V"))
|
directions=("V", "V"))
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_stack_center(from_layer="m2",
|
||||||
offset=pin_offset)
|
to_layer="m4",
|
||||||
self.add_via_center(layers=self.m3_stack,
|
offset=pin_offset)
|
||||||
offset=pin_offset)
|
|
||||||
|
|
||||||
bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size)]
|
bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size)]
|
||||||
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
for x in bank_names:
|
for x in bank_names:
|
||||||
|
pin = self.bank_inst.get_pin(x)
|
||||||
if port % 2:
|
if port % 2:
|
||||||
pin_offset = self.bank_inst.get_pin(x).uc()
|
pin_offset = pin.uc()
|
||||||
else:
|
else:
|
||||||
pin_offset = self.bank_inst.get_pin(x).bc()
|
pin_offset = pin.bc()
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
offset=pin_offset)
|
to_layer="m4",
|
||||||
self.add_via_center(layers=self.m2_stack,
|
offset=pin_offset)
|
||||||
offset=pin_offset)
|
|
||||||
self.add_via_center(layers=self.m3_stack,
|
|
||||||
offset=pin_offset)
|
|
||||||
|
|
||||||
route_map = list(zip(bank_pins, dff_pins))
|
route_map = list(zip(bank_pins, dff_pins))
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
layer_stack = self.m3_stack
|
layer_stack = self.m3_stack
|
||||||
else:
|
else:
|
||||||
layer_stack = self.m1_stack
|
layer_stack = self.m1_stack
|
||||||
|
|
||||||
self.create_horizontal_channel_route(netlist=route_map,
|
self.create_horizontal_channel_route(netlist=route_map,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layer_stack=layer_stack)
|
layer_stack=layer_stack)
|
||||||
|
|
@ -409,9 +421,9 @@ class sram_1bank(sram_base):
|
||||||
# This is where the channel will start (y-dimension at least)
|
# This is where the channel will start (y-dimension at least)
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
if port % 2:
|
if port % 2:
|
||||||
offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks + 2) * self.m1_pitch)
|
offset = self.wmask_dff_insts[port].ll() - vector(0, self.wmask_bus_size)
|
||||||
else:
|
else:
|
||||||
offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
offset = self.wmask_dff_insts[port].ul() + vector(0, self.wmask_bus_gap)
|
||||||
|
|
||||||
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
|
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
|
||||||
dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names]
|
dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names]
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#
|
#
|
||||||
import datetime
|
import datetime
|
||||||
import debug
|
import debug
|
||||||
|
from math import log
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS, print_time
|
from globals import OPTS, print_time
|
||||||
|
|
@ -137,7 +138,6 @@ class sram_base(design, verilog, lef):
|
||||||
self.copy_power_pins(inst, "vdd")
|
self.copy_power_pins(inst, "vdd")
|
||||||
self.copy_power_pins(inst, "gnd")
|
self.copy_power_pins(inst, "gnd")
|
||||||
|
|
||||||
import tech
|
|
||||||
if not OPTS.route_supplies:
|
if not OPTS.route_supplies:
|
||||||
# Do not route the power supply (leave as must-connect pins)
|
# Do not route the power supply (leave as must-connect pins)
|
||||||
return
|
return
|
||||||
|
|
@ -148,6 +148,7 @@ class sram_base(design, verilog, lef):
|
||||||
grid_stack = power_grid
|
grid_stack = power_grid
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# if no power_grid is specified by tech we use sensible defaults
|
# if no power_grid is specified by tech we use sensible defaults
|
||||||
|
import tech
|
||||||
if "m4" in tech.layer:
|
if "m4" in tech.layer:
|
||||||
# Route a M3/M4 grid
|
# Route a M3/M4 grid
|
||||||
grid_stack = self.m3_stack
|
grid_stack = self.m3_stack
|
||||||
|
|
@ -497,70 +498,6 @@ class sram_base(design, verilog, lef):
|
||||||
|
|
||||||
return insts
|
return insts
|
||||||
|
|
||||||
def connect_vbus_m2m3(self, src_pin, dest_pin):
|
|
||||||
"""
|
|
||||||
Helper routine to connect an instance to a vertical bus.
|
|
||||||
Routes horizontal then vertical L shape.
|
|
||||||
Dest pin is assumed to be on M2.
|
|
||||||
Src pin can be on M1/M2/M3.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if src_pin.cx()<dest_pin.cx():
|
|
||||||
in_pos = src_pin.rc()
|
|
||||||
else:
|
|
||||||
in_pos = src_pin.lc()
|
|
||||||
if src_pin.cy() < dest_pin.cy():
|
|
||||||
out_pos = dest_pin.bc()
|
|
||||||
else:
|
|
||||||
out_pos = dest_pin.uc()
|
|
||||||
|
|
||||||
# move horizontal first
|
|
||||||
self.add_wire(("m3", "via2", "m2"),
|
|
||||||
[in_pos,
|
|
||||||
vector(out_pos.x, in_pos.y),
|
|
||||||
out_pos])
|
|
||||||
if src_pin.layer=="m1":
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=in_pos)
|
|
||||||
if src_pin.layer in ["m1", "m2"]:
|
|
||||||
self.add_via_center(layers=self.m2_stack,
|
|
||||||
offset=in_pos)
|
|
||||||
|
|
||||||
def connect_hbus_m2m3(self, src_pin, dest_pin):
|
|
||||||
"""
|
|
||||||
Helper routine to connect an instance to a horizontal bus.
|
|
||||||
Routes horizontal then vertical L shape.
|
|
||||||
Dest pin is on M1/M2/M3.
|
|
||||||
Src pin can be on M1/M2/M3.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if src_pin.cx()<dest_pin.cx():
|
|
||||||
in_pos = src_pin.rc()
|
|
||||||
else:
|
|
||||||
in_pos = src_pin.lc()
|
|
||||||
if src_pin.cy() < dest_pin.cy():
|
|
||||||
out_pos = dest_pin.lc()
|
|
||||||
else:
|
|
||||||
out_pos = dest_pin.rc()
|
|
||||||
|
|
||||||
# move horizontal first
|
|
||||||
self.add_wire(("m3", "via2", "m2"),
|
|
||||||
[in_pos,
|
|
||||||
vector(out_pos.x, in_pos.y),
|
|
||||||
out_pos])
|
|
||||||
if src_pin.layer=="m1":
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=in_pos)
|
|
||||||
if src_pin.layer in ["m1", "m2"]:
|
|
||||||
self.add_via_center(layers=self.m2_stack,
|
|
||||||
offset=in_pos)
|
|
||||||
if dest_pin.layer=="m1":
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=out_pos)
|
|
||||||
if dest_pin.layer=="m3":
|
|
||||||
self.add_via_center(layers=self.m2_stack,
|
|
||||||
offset=out_pos)
|
|
||||||
|
|
||||||
def sp_write(self, sp_name, lvs_netlist=False):
|
def sp_write(self, sp_name, lvs_netlist=False):
|
||||||
# Write the entire spice of the object to the file
|
# Write the entire spice of the object to the file
|
||||||
############################################################
|
############################################################
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ class ptx_3finger_nmos_test(openram_test):
|
||||||
width=tech.drc["minwidth_tx"],
|
width=tech.drc["minwidth_tx"],
|
||||||
mults=3,
|
mults=3,
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
connect_active=True,
|
connect_source_active=True,
|
||||||
|
connect_drain_active=True,
|
||||||
connect_poly=True)
|
connect_poly=True)
|
||||||
self.local_drc_check(fet)
|
self.local_drc_check(fet)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ class ptx_3finger_pmos_test(openram_test):
|
||||||
width=tech.drc["minwidth_tx"],
|
width=tech.drc["minwidth_tx"],
|
||||||
mults=3,
|
mults=3,
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
connect_active=True,
|
connect_source_active=True,
|
||||||
|
connect_drain_active=True,
|
||||||
connect_poly=True)
|
connect_poly=True)
|
||||||
self.local_drc_check(fet)
|
self.local_drc_check(fet)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ class ptx_4finger_nmos_test(openram_test):
|
||||||
width= tech.drc["minwidth_tx"],
|
width= tech.drc["minwidth_tx"],
|
||||||
mults=4,
|
mults=4,
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
connect_active=True,
|
connect_source_active=True,
|
||||||
|
connect_drain_active=True,
|
||||||
connect_poly=True)
|
connect_poly=True)
|
||||||
self.local_drc_check(fet)
|
self.local_drc_check(fet)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ class ptx_test(openram_test):
|
||||||
width=tech.drc["minwidth_tx"],
|
width=tech.drc["minwidth_tx"],
|
||||||
mults=4,
|
mults=4,
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
connect_active=True,
|
connect_source_active=True,
|
||||||
|
connect_drain_active=True,
|
||||||
connect_poly=True)
|
connect_poly=True)
|
||||||
self.local_drc_check(fet)
|
self.local_drc_check(fet)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 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 unittest
|
||||||
|
from testutils import *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class ptx_no_contacts_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
import tech
|
||||||
|
|
||||||
|
debug.info(2, "Checking single finger no source/drain")
|
||||||
|
fet = factory.create(module_type="ptx",
|
||||||
|
width=tech.drc["minwidth_tx"],
|
||||||
|
mults=1,
|
||||||
|
add_source_contact=False,
|
||||||
|
add_drain_contact=False,
|
||||||
|
tx_type="nmos")
|
||||||
|
self.local_drc_check(fet)
|
||||||
|
|
||||||
|
debug.info(2, "Checking multifinger no source/drain")
|
||||||
|
fet = factory.create(module_type="ptx",
|
||||||
|
width=tech.drc["minwidth_tx"],
|
||||||
|
mults=4,
|
||||||
|
add_source_contact=False,
|
||||||
|
add_drain_contact=False,
|
||||||
|
tx_type="nmos")
|
||||||
|
self.local_drc_check(fet)
|
||||||
|
|
||||||
|
debug.info(2, "Checking series ptx")
|
||||||
|
fet = factory.create(module_type="ptx",
|
||||||
|
width=tech.drc["minwidth_tx"],
|
||||||
|
mults=4,
|
||||||
|
series_devices=True,
|
||||||
|
tx_type="nmos")
|
||||||
|
self.local_drc_check(fet)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main(testRunner=debugTestRunner())
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 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 unittest
|
||||||
|
from testutils import *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class pinv_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
debug.info(2, "Checking 100x inverter")
|
||||||
|
tx = factory.create(module_type="pinv", size=100)
|
||||||
|
self.local_check(tx)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main(testRunner=debugTestRunner())
|
||||||
|
|
@ -21,35 +21,32 @@ class hierarchical_decoder_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
# check hierarchical decoder for single port
|
# Checks 2x4 and 2-input NAND decoder
|
||||||
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
|
# Checks 2x4 and 2-input NAND decoder with non-power-of-two
|
||||||
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
|
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
debug.info(1, "Testing 23 row sample for hierarchical_decoder")
|
# Checks 2x4 with 3x8 and 2-input NAND decoder
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
|
|
||||||
self.local_check(a)
|
|
||||||
|
|
||||||
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
|
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
debug.info(1, "Testing 65 row sample for hierarchical_decoder")
|
# Checks 3 x 2x4 and 3-input NAND decoder
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
|
debug.info(1, "Testing 64 row sample for hierarchical_decoder")
|
||||||
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=64)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
debug.info(1, "Testing 128 row sample for hierarchical_decoder")
|
# Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
|
debug.info(1, "Testing 132 row sample for hierarchical_decoder")
|
||||||
self.local_check(a)
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=132)
|
||||||
|
|
||||||
debug.info(1, "Testing 341 row sample for hierarchical_decoder")
|
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
|
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
|
# Checks 3 x 3x8 and 3-input NAND decoder
|
||||||
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
|
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 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 unittest
|
||||||
|
from testutils import *
|
||||||
|
import sys, os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class control_logic_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
debug.info(1, "Testing sample for control_logic_r")
|
||||||
|
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="r")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main(testRunner=debugTestRunner())
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 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 unittest
|
||||||
|
from testutils import *
|
||||||
|
import sys, os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class control_logic_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
debug.info(1, "Testing sample for control_logic_rw")
|
||||||
|
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main(testRunner=debugTestRunner())
|
||||||
|
|
@ -20,21 +20,13 @@ class control_logic_test(openram_test):
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
import control_logic
|
|
||||||
import tech
|
|
||||||
|
|
||||||
debug.info(1, "Testing sample for control_logic_rw")
|
|
||||||
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32)
|
|
||||||
self.local_check(a)
|
|
||||||
|
|
||||||
debug.info(1, "Testing sample for control_logic_r")
|
|
||||||
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="r")
|
|
||||||
self.local_check(a)
|
|
||||||
|
|
||||||
debug.info(1, "Testing sample for control_logic_w")
|
debug.info(1, "Testing sample for control_logic_w")
|
||||||
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="w")
|
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="w")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
# run the test from the command line
|
# run the test from the command line
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
(OPTS, args) = globals.parse_args()
|
(OPTS, args) = globals.parse_args()
|
||||||
|
|
@ -25,28 +25,28 @@ class single_bank_test(openram_test):
|
||||||
c = sram_config(word_size=4,
|
c = sram_config(word_size=4,
|
||||||
num_words=16)
|
num_words=16)
|
||||||
|
|
||||||
c.words_per_row=1
|
# c.words_per_row=1
|
||||||
factory.reset()
|
# factory.reset()
|
||||||
c.recompute_sizes()
|
# c.recompute_sizes()
|
||||||
debug.info(1, "No column mux")
|
# debug.info(1, "No column mux")
|
||||||
a = factory.create("bank", sram_config=c)
|
# a = factory.create("bank", sram_config=c)
|
||||||
self.local_check(a)
|
# self.local_check(a)
|
||||||
|
|
||||||
c.num_words=32
|
# c.num_words=32
|
||||||
c.words_per_row=2
|
# c.words_per_row=2
|
||||||
factory.reset()
|
# factory.reset()
|
||||||
c.recompute_sizes()
|
# c.recompute_sizes()
|
||||||
debug.info(1, "Two way column mux")
|
# debug.info(1, "Two way column mux")
|
||||||
a = factory.create("bank", sram_config=c)
|
# a = factory.create("bank", sram_config=c)
|
||||||
self.local_check(a)
|
# self.local_check(a)
|
||||||
|
|
||||||
c.num_words=64
|
# c.num_words=64
|
||||||
c.words_per_row=4
|
# c.words_per_row=4
|
||||||
factory.reset()
|
# factory.reset()
|
||||||
c.recompute_sizes()
|
# c.recompute_sizes()
|
||||||
debug.info(1, "Four way column mux")
|
# debug.info(1, "Four way column mux")
|
||||||
a = factory.create("bank", sram_config=c)
|
# a = factory.create("bank", sram_config=c)
|
||||||
self.local_check(a)
|
# self.local_check(a)
|
||||||
|
|
||||||
c.word_size=2
|
c.word_size=2
|
||||||
c.num_words=128
|
c.num_words=128
|
||||||
|
|
|
||||||
|
|
@ -70,20 +70,20 @@ class timing_sram_test(openram_test):
|
||||||
'min_period': 0.898,
|
'min_period': 0.898,
|
||||||
'disabled_write1_power': [0.201411]}
|
'disabled_write1_power': [0.201411]}
|
||||||
elif OPTS.tech_name == "scn4m_subm":
|
elif OPTS.tech_name == "scn4m_subm":
|
||||||
golden_data = {'read1_power': [12.11658],
|
golden_data = {'delay_hl': [1.8435739999999998],
|
||||||
'write1_power': [10.52653],
|
'delay_lh': [1.8435739999999998],
|
||||||
'read0_power': [11.956710000000001],
|
'disabled_read0_power': [5.917947],
|
||||||
'disabled_write0_power': [7.673665],
|
'disabled_read1_power': [7.154297],
|
||||||
'disabled_write1_power': [7.981922000000001],
|
'disabled_write0_power': [7.696351],
|
||||||
'slew_lh': [1.868836],
|
'disabled_write1_power': [7.999409000000001],
|
||||||
'slew_hl': [1.868836],
|
'leakage_power': 0.004809726,
|
||||||
'delay_hl': [1.8598510000000001],
|
'min_period': 6.875,
|
||||||
'delay_lh': [1.8598510000000001],
|
'read0_power': [11.833079999999999],
|
||||||
'leakage_power': 0.008613619,
|
'read1_power': [11.99236],
|
||||||
'disabled_read0_power': [5.904712],
|
'slew_hl': [1.8668490000000002],
|
||||||
'min_period': 6.875,
|
'slew_lh': [1.8668490000000002],
|
||||||
'disabled_read1_power': [7.132159],
|
'write0_power': [13.287510000000001],
|
||||||
'write0_power': [13.406400000000001]}
|
'write1_power': [10.416369999999999]}
|
||||||
else:
|
else:
|
||||||
self.assertTrue(False) # other techs fail
|
self.assertTrue(False) # other techs fail
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ tech_modules = module_type()
|
||||||
cell_properties = cell_properties()
|
cell_properties = cell_properties()
|
||||||
cell_properties.bitcell.mirror.x = True
|
cell_properties.bitcell.mirror.x = True
|
||||||
cell_properties.bitcell.mirror.y = False
|
cell_properties.bitcell.mirror.y = False
|
||||||
|
cell_properties.bitcell_power_pin_directions = ("V", "V")
|
||||||
|
|
||||||
###################################################
|
###################################################
|
||||||
# GDS file info
|
# GDS file info
|
||||||
|
|
@ -63,6 +63,13 @@ m1_stack = ("m1", "via1", "m2")
|
||||||
m2_stack = ("m2", "via2", "m3")
|
m2_stack = ("m2", "via2", "m3")
|
||||||
m3_stack = ("m3", "via3", "m4")
|
m3_stack = ("m3", "via3", "m4")
|
||||||
|
|
||||||
|
layer_indices = {"poly": 0,
|
||||||
|
"active": 0,
|
||||||
|
"m1": 1,
|
||||||
|
"m2": 2,
|
||||||
|
"m3": 3,
|
||||||
|
"m4": 4}
|
||||||
|
|
||||||
# The FEOL stacks get us up to m1
|
# The FEOL stacks get us up to m1
|
||||||
feol_stacks = [poly_stack,
|
feol_stacks = [poly_stack,
|
||||||
active_stack]
|
active_stack]
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,13 @@ m1_stack = ("m1", "via1", "m2")
|
||||||
m2_stack = ("m2", "via2", "m3")
|
m2_stack = ("m2", "via2", "m3")
|
||||||
m3_stack = ("m3", "via3", "m4")
|
m3_stack = ("m3", "via3", "m4")
|
||||||
|
|
||||||
|
layer_indices = {"poly": 0,
|
||||||
|
"active": 0,
|
||||||
|
"m1": 1,
|
||||||
|
"m2": 2,
|
||||||
|
"m3": 3,
|
||||||
|
"m4": 4}
|
||||||
|
|
||||||
# The FEOL stacks get us up to m1
|
# The FEOL stacks get us up to m1
|
||||||
feol_stacks = [poly_stack,
|
feol_stacks = [poly_stack,
|
||||||
active_stack]
|
active_stack]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue