mirror of https://github.com/VLSIDA/OpenRAM.git
Improved ptx code but removed internal active/poly positions.
This commit is contained in:
parent
09ca8ba17d
commit
7ff82a2aed
|
|
@ -512,10 +512,10 @@ class bank(design.design):
|
|||
|
||||
bank_sel_line_pos = vector(xoffset_bank_sel + 0.5*self.m2_width, self.min_point)
|
||||
bank_sel_pin_pos=vector(self.left_vdd_x_offset, self.min_point)
|
||||
self.add_center_layout_pin(text="bank_sel",
|
||||
layer="metal3",
|
||||
start=bank_sel_pin_pos,
|
||||
end=bank_sel_line_pos)
|
||||
self.add_center_layout_pin_segment(text="bank_sel",
|
||||
layer="metal3",
|
||||
start=bank_sel_pin_pos,
|
||||
end=bank_sel_line_pos)
|
||||
self.add_center_via(layers=("metal2","via2","metal3"),
|
||||
offset=bank_sel_line_pos,
|
||||
rotate=90)
|
||||
|
|
@ -629,10 +629,10 @@ class bank(design.design):
|
|||
offset=logic_pos,
|
||||
rotate=90)
|
||||
|
||||
self.add_center_layout_pin(text=input_name,
|
||||
layer="metal3",
|
||||
start=input_pos,
|
||||
end=logic_pos)
|
||||
self.add_center_layout_pin_segment(text=input_name,
|
||||
layer="metal3",
|
||||
start=input_pos,
|
||||
end=logic_pos)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -117,8 +117,23 @@ class layout:
|
|||
return self.objs[-1]
|
||||
return None
|
||||
|
||||
def add_center_rect(self, layer, start, end):
|
||||
""" Add a min-width rectangle from centered on the start and end points"""
|
||||
def add_center_rect(self, layer, offset, width=0, height=0):
|
||||
"""Adds a rectangle on a given layer at the center point with width and height"""
|
||||
if width==0:
|
||||
width=drc["minwidth_{}".format(layer)]
|
||||
if height==0:
|
||||
height=drc["minwidth_{}".format(layer)]
|
||||
# negative layers indicate "unused" layers in a given technology
|
||||
layerNumber = techlayer[layer]
|
||||
corrected_offset = offset - vector(0.5*width,0.5*height)
|
||||
if layerNumber >= 0:
|
||||
self.objs.append(geometry.rectangle(layerNumber, corrected_offset, width, height))
|
||||
return self.objs[-1]
|
||||
return None
|
||||
|
||||
|
||||
def add_center_segment(self, layer, start, end):
|
||||
""" Add a min-width rectanglular segment using center line on the start to end point """
|
||||
minwidth_layer = drc["minwidth_{}".format(layer)]
|
||||
if start.x!=end.x and start.y!=end.y:
|
||||
debug.error("Nonrectilinear center rect!",-1)
|
||||
|
|
@ -159,7 +174,7 @@ class layout:
|
|||
new_name = pin.name
|
||||
self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height())
|
||||
|
||||
def add_center_layout_pin(self, text, layer, start, end):
|
||||
def add_center_layout_pin_segment(self, text, layer, start, end):
|
||||
""" Creates a path like pin with center-line convention """
|
||||
|
||||
debug.check(start.x==end.x or start.y==end.y,"Cannot have a non-manhatten layout pin.")
|
||||
|
|
@ -182,7 +197,19 @@ class layout:
|
|||
|
||||
|
||||
return self.add_layout_pin(text, layer, ll_offset, width, height)
|
||||
|
||||
|
||||
def add_center_layout_pin_rect(self, text, layer, offset, width=None, height=None):
|
||||
""" Creates a path like pin with center-line convention """
|
||||
if width==None:
|
||||
width=drc["minwidth_{0}".format(layer)]
|
||||
if height==None:
|
||||
height=drc["minwidth_{0}".format(layer)]
|
||||
|
||||
ll_offset = offset - vector(0.5*width,0.5*height)
|
||||
|
||||
return self.add_layout_pin(text, layer, ll_offset, width, height)
|
||||
|
||||
|
||||
def remove_layout_pin(self, text):
|
||||
"""Delete a labeled pin (or all pins of the same name)"""
|
||||
self.pin_map[text]=[]
|
||||
|
|
@ -287,11 +314,11 @@ class layout:
|
|||
|
||||
def add_center_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
|
||||
""" This is just an alias for a via."""
|
||||
return self.add_centered_via(layers=layers,
|
||||
offset=offset,
|
||||
size=size,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
return self.add_center_via(layers=layers,
|
||||
offset=offset,
|
||||
size=size,
|
||||
mirror=mirror,
|
||||
rotate=rotate)
|
||||
|
||||
def add_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
|
||||
""" Add a three layer via structure. """
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class options(optparse.Values):
|
|||
tech_name = ""
|
||||
# This is the temp directory where all intermediate results are stored.
|
||||
openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid())
|
||||
openram_temp = "/Users/mrg/openram_temp/"
|
||||
# This is the verbosity level to control debug information. 0 is none, 1
|
||||
# is minimal, etc.
|
||||
debug_level = 0
|
||||
|
|
|
|||
493
compiler/ptx.py
493
compiler/ptx.py
|
|
@ -8,11 +8,18 @@ import re
|
|||
|
||||
class ptx(design.design):
|
||||
"""
|
||||
This module generates gds and spice of a parametrically NMOS or PMOS sized transistor.
|
||||
Creates a simple MOS transistor. poly_positions are the ll of the poly gate. active_contact_positions
|
||||
is an array of the positions of the ll of active contacts (left to right)
|
||||
This module generates gds and spice of a parametrically NMOS or
|
||||
PMOS sized transistor. Pins are accessed as D, G, S, B. Width is
|
||||
the transistor width. Mults is the number of transistors of the
|
||||
given width. Total width is therefore mults*width. Options allow
|
||||
you to connect the fingered gates and active for parallel devices.
|
||||
|
||||
"""
|
||||
def __init__(self, width=drc["minwidth_tx"], mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None):
|
||||
# We need to keep unique names because outputting to GDSII
|
||||
# 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
|
||||
# have poly connected, for example.
|
||||
name = "{0}_m{1}_w{2}".format(tx_type, mults, width)
|
||||
if connect_active:
|
||||
name += "_a"
|
||||
|
|
@ -20,22 +27,24 @@ class ptx(design.design):
|
|||
name += "_p"
|
||||
if num_contacts:
|
||||
name += "_c{}".format(num_contacts)
|
||||
name=re.sub('\.','_',name) # remove periods for newer spice compatibility
|
||||
# replace periods with underscore for newer spice compatibility
|
||||
name=re.sub('\.','_',name)
|
||||
|
||||
design.design.__init__(self, name)
|
||||
debug.info(3, "create ptx structure {0}".format(name))
|
||||
debug.info(3, "create ptx2 structure {0}".format(name))
|
||||
|
||||
self.tx_type = tx_type
|
||||
self.mults = mults
|
||||
self.gate_width = width
|
||||
self.tx_width = width
|
||||
self.connect_active = connect_active
|
||||
self.connect_poly = connect_poly
|
||||
self.num_contacts = num_contacts
|
||||
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.create_spice()
|
||||
self.create_layout()
|
||||
# for run-time, we won't check every transitor DRC independently
|
||||
# but this may be uncommented for debug purposes
|
||||
#self.DRC()
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -43,27 +52,14 @@ class ptx(design.design):
|
|||
|
||||
def create_layout(self):
|
||||
self.setup_layout_constants()
|
||||
|
||||
if self.num_contacts==None:
|
||||
self.num_contacts=self.calculate_num_contacts()
|
||||
|
||||
# This is not actually instantiated but used for calculations
|
||||
self.active_contact = contact(layer_stack=("active", "contact", "metal1"),
|
||||
dimensions=(1, self.num_contacts))
|
||||
|
||||
self.add_active()
|
||||
self.add_implants()
|
||||
self.add_well_implant()
|
||||
self.add_poly()
|
||||
self.add_active_contacts()
|
||||
|
||||
# offset this BEFORE we add the active/poly connections
|
||||
self.offset_all_coordinates()
|
||||
#self.offset_all_coordinates()
|
||||
|
||||
if self.connect_active:
|
||||
self.connect_fingered_active()
|
||||
if self.connect_poly:
|
||||
self.connect_fingered_poly()
|
||||
|
||||
def offset_attributes(self, coordinate):
|
||||
"""Translates all stored 2d cartesian coordinates within the
|
||||
attr dictionary"""
|
||||
|
|
@ -95,17 +91,17 @@ class ptx(design.design):
|
|||
if isinstance(attr_val, vector):
|
||||
setattr(self, attr_key, vector(attr_val - coordinate))
|
||||
|
||||
def offset_all_coordinates(self):
|
||||
offset = self.find_lowest_coords()
|
||||
self.offset_attributes(offset)
|
||||
self.translate_all(offset)
|
||||
# def offset_all_coordinates(self):
|
||||
# offset = self.find_lowest_coords()
|
||||
# self.offset_attributes(offset)
|
||||
# self.translate_all(offset)
|
||||
|
||||
# We can do this in ptx because we have offset all modules it uses.
|
||||
# Is this really true considering the paths that connect the src/drain?
|
||||
self.height = max(max(obj.offset.y + obj.height for obj in self.objs),
|
||||
max(inst.offset.y + inst.mod.height for inst in self.insts))
|
||||
self.width = max(max(obj.offset.x + obj.width for obj in self.objs),
|
||||
max(inst.offset.x + inst.mod.width for inst in self.insts))
|
||||
# # We can do this in ptx because we have offset all modules it uses.
|
||||
# # Is this really true considering the paths that connect the src/drain?
|
||||
# self.height = max(max(obj.offset.y + obj.height for obj in self.objs),
|
||||
# max(inst.offset.y + inst.mod.height for inst in self.insts))
|
||||
# self.width = max(max(obj.offset.x + obj.width for obj in self.objs),
|
||||
# max(inst.offset.x + inst.mod.width for inst in self.insts))
|
||||
|
||||
def create_spice(self):
|
||||
self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
|
||||
|
|
@ -114,60 +110,109 @@ class ptx(design.design):
|
|||
" ".join(self.pins),
|
||||
spice[self.tx_type],
|
||||
self.mults,
|
||||
self.gate_width,
|
||||
self.tx_width,
|
||||
drc["minwidth_poly"]))
|
||||
self.spice.append(".ENDS {0}".format(self.name))
|
||||
|
||||
def setup_layout_constants(self):
|
||||
# usually contacted poly will limit the spacing, but it could be poly
|
||||
# spacing in some weird technology.
|
||||
self.mults_poly_to_poly = max(2 * drc["contact_to_poly"] + drc["minwidth_contact"],
|
||||
drc["poly_to_poly"])
|
||||
outeractive_to_contact = max(drc["active_enclosure_contact"],
|
||||
(drc["minwidth_active"] - drc["minwidth_contact"]) / 2)
|
||||
self.active_width = (2 * (outeractive_to_contact + drc["minwidth_contact"]
|
||||
+ drc["contact_to_poly"])
|
||||
+ drc["minwidth_poly"]
|
||||
+ (self.mults - 1) * (self.mults_poly_to_poly
|
||||
+ drc["minwidth_poly"]))
|
||||
self.active_height = max(drc["minarea_active"] / self.active_width,
|
||||
self.gate_width)
|
||||
self.poly_width = drc["minwidth_poly"] # horizontal
|
||||
self.poly_height = max(drc["minarea_poly"] / self.poly_width,
|
||||
self.gate_width
|
||||
+ 2 * drc["poly_extend_active"]) # vertical
|
||||
self.well_width = (self.active_width
|
||||
+ 2 * (drc["well_enclosure_active"]))
|
||||
self.well_height = max(self.gate_width + 2 * (drc["well_enclosure_active"]),
|
||||
"""
|
||||
Pre-compute some handy layout parameters.
|
||||
"""
|
||||
|
||||
if self.num_contacts==None:
|
||||
self.num_contacts=self.calculate_num_contacts()
|
||||
|
||||
# This is not actually instantiated but used for calculations
|
||||
self.active_contact = contact(layer_stack=("active", "contact", "metal1"),
|
||||
dimensions=(1, self.num_contacts))
|
||||
|
||||
# Standard DRC rules
|
||||
self.active_width = drc["minwidth_active"]
|
||||
self.contact_width = drc["minwidth_contact"]
|
||||
self.poly_width = drc["minwidth_poly"]
|
||||
self.poly_to_active = drc["poly_to_active"]
|
||||
self.poly_extend_active = drc["poly_extend_active"]
|
||||
|
||||
# The contacted poly pitch (or uncontacted in an odd technology)
|
||||
self.poly_pitch = max(2*drc["contact_to_poly"] + self.contact_width + self.poly_width,
|
||||
drc["poly_to_poly"])
|
||||
|
||||
# The contacted poly pitch (or uncontacted in an odd technology)
|
||||
self.contact_pitch = 2*drc["contact_to_poly"] + self.contact_width + self.poly_width
|
||||
|
||||
# The enclosure of an active contact. Not sure about second term.
|
||||
active_enclose_contact = max(drc["active_enclosure_contact"],
|
||||
(self.active_width - self.contact_width)/2)
|
||||
# This is the distance from the edge of poly to the contacted end of active
|
||||
self.end_to_poly = active_enclose_contact + self.contact_width + drc["contact_to_poly"]
|
||||
|
||||
|
||||
# Active width is determined by enclosure on both ends and contacted pitch,
|
||||
# at least one poly and n-1 poly pitches
|
||||
self.active_width = 2*self.end_to_poly + self.poly_width + (self.mults - 1)*self.poly_pitch
|
||||
|
||||
# Active height is just the transistor width
|
||||
self.active_height = self.tx_width
|
||||
|
||||
# Poly height must include poly extension over active
|
||||
self.poly_height = self.tx_width + 2*self.poly_extend_active
|
||||
|
||||
# Well enclosure of active, ensure minwidth as well
|
||||
self.well_width = max(self.active_width + 2*drc["well_enclosure_active"],
|
||||
drc["minwidth_well"])
|
||||
self.well_height = max(self.tx_width + 2*drc["well_enclosure_active"],
|
||||
drc["minwidth_well"])
|
||||
|
||||
# The active offset is due to the well extension
|
||||
self.active_offset = vector([drc["well_enclosure_active"]]*2)
|
||||
|
||||
def connect_fingered_poly(self):
|
||||
poly_connect_length = self.poly_positions[-1].x + self.poly_width \
|
||||
- self.poly_positions[0].x
|
||||
poly_connect_position = self.poly_positions[0] - vector(0, self.poly_width)
|
||||
if len(self.poly_positions) > 1:
|
||||
self.remove_layout_pin("G") # only keep the main pin
|
||||
self.add_layout_pin(text="G",
|
||||
layer="poly",
|
||||
offset=poly_connect_position,
|
||||
width=poly_connect_length,
|
||||
height=drc["minwidth_poly"])
|
||||
# This is the center of the first active contact offset (centered vertically)
|
||||
self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5*self.contact_width,
|
||||
0.5*self.active_height)
|
||||
|
||||
|
||||
# Min area results are just flagged for now.
|
||||
debug.check(self.active_width*self.active_height>=drc["minarea_active"],"Minimum active area violated.")
|
||||
# We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue.
|
||||
debug.check(self.poly_width*self.poly_height>=drc["minarea_poly"],"Minimum poly area violated.")
|
||||
|
||||
def connect_fingered_poly(self, poly_positions):
|
||||
"""
|
||||
Connect together the poly gates and create the single gate pin.
|
||||
The poly positions are the center of the poly gates
|
||||
and we will add a single horizontal connection.
|
||||
"""
|
||||
# Nothing to do if there's one poly gate
|
||||
if len(poly_positions) == 1:
|
||||
return
|
||||
|
||||
# The width of the poly is from the left-most to right-most poly gate
|
||||
poly_width = poly_positions[-1].x - poly_positions[0].x + self.poly_width
|
||||
# This can be limited by poly to active spacing or the poly extension
|
||||
distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height)
|
||||
poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active)
|
||||
|
||||
# Remove the old pin and add the new one
|
||||
self.remove_layout_pin("G") # only keep the main pin
|
||||
self.add_layout_pin(text="G",
|
||||
layer="poly",
|
||||
offset=poly_offset,
|
||||
width=poly_width,
|
||||
height=drc["minwidth_poly"])
|
||||
|
||||
self.poly_connect_index = len(self.objs) - 1
|
||||
|
||||
|
||||
|
||||
def pairwise(self, iterable):
|
||||
#"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
||||
"""
|
||||
Create an iterable set of pairs from a set:
|
||||
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
||||
"""
|
||||
from itertools import tee, izip
|
||||
a, b = tee(iterable)
|
||||
next(b, None)
|
||||
return izip(a, b)
|
||||
|
||||
def determine_active_wire_location(self):
|
||||
self.determine_source_wire()
|
||||
self.determine_drain_wire()
|
||||
|
||||
def determine_source_wire(self):
|
||||
self.source_positions = []
|
||||
|
|
@ -185,13 +230,14 @@ class ptx(design.design):
|
|||
connected.y + 0.5 * drc["minwidth_metal2"]))
|
||||
self.source_positions.append(b + correct)
|
||||
|
||||
def determine_drain_wire(self):
|
||||
self.drain_positions = []
|
||||
drain_contact_pos = self.active_contact_positions[1:][::2] # odd indexes
|
||||
return source_positions
|
||||
|
||||
def determine_drain_wire(self, contact_positions):
|
||||
drain_positions = []
|
||||
drain_contact_pos = contact_positions[1:][::2] # odd indexes
|
||||
for c, d in self.pairwise(drain_contact_pos):
|
||||
correct = vector(0.5*(self.active_contact.width
|
||||
- drc["minwidth_metal1"]
|
||||
+ drc["minwidth_contact"]),
|
||||
- drc["minwidth_metal1"] + drc["minwidth_contact"]),
|
||||
0.5*(self.active_contact.height - drc["minwidth_contact"])
|
||||
- drc["metal1_extend_contact"])
|
||||
connected = vector(d.x + drc["minwidth_metal1"], c.y - drc["metal1_to_metal1"])
|
||||
|
|
@ -201,194 +247,185 @@ class ptx(design.design):
|
|||
connected.y - 0.5 * drc["minwidth_metal1"]))
|
||||
self.drain_positions.append(vector(d + correct))
|
||||
|
||||
def connect_fingered_active(self):
|
||||
self.determine_active_wire_location()
|
||||
# allows one to connect the source and drains
|
||||
self.source_connect_index = None
|
||||
if self.source_positions:
|
||||
return drain_positions
|
||||
|
||||
|
||||
def connect_fingered_active(self, drain_positions, source_positions):
|
||||
"""
|
||||
Connect each contact up/down to a source or drain pin
|
||||
"""
|
||||
|
||||
# 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
|
||||
pin_offset = vector(0, 0.5*self.active_contact.second_layer_height \
|
||||
+ drc["metal1_to_metal1"] + 0.5*drc["minwidth_metal1"])
|
||||
# This is the width of a contact to extend the ends of the pin
|
||||
end_offset = vector(self.active_contact.second_layer_width/2,0)
|
||||
|
||||
if source_positions: # if not an empty set
|
||||
self.remove_layout_pin("S") # remove the individual connections
|
||||
self.source_positions = path.create_rectilinear_route(self.source_positions)
|
||||
self.add_path(("metal1"), self.source_positions)
|
||||
# The source positions are odd since the second one is always redundant
|
||||
# so we must use the THIRD one
|
||||
self.add_center_layout_pin(text="S",
|
||||
layer="metal1",
|
||||
start=self.source_positions[2]-vector(0.5*drc["minwidth_metal1"],0),
|
||||
end=self.source_positions[-2]+vector(0.5*drc["minwidth_metal1"],0))
|
||||
self.source_connect_index = len(self.insts) - 1
|
||||
self.drain_connect_index = None
|
||||
if self.drain_positions:
|
||||
# Add each vertical segment
|
||||
for a in source_positions:
|
||||
self.add_path(("metal1"), [a,a+pin_offset])
|
||||
# Add a single horizontal pin
|
||||
self.add_center_layout_pin_segment(text="S",
|
||||
layer="metal1",
|
||||
start=source_positions[0]+pin_offset-end_offset,
|
||||
end=source_positions[-1]+pin_offset+end_offset)
|
||||
|
||||
if drain_positions: # if not an empty set
|
||||
self.remove_layout_pin("D") # remove the individual connections
|
||||
self.drain_positions = path.create_rectilinear_route(self.drain_positions)
|
||||
self.add_path(("metal1"), self.drain_positions)
|
||||
# The source positions are odd since the second one is always redundant
|
||||
# so we must use the THIRD one
|
||||
self.add_center_layout_pin(text="D",
|
||||
layer="metal1",
|
||||
start=self.drain_positions[2]-vector(0.5*drc["minwidth_metal1"],0),
|
||||
end=self.drain_positions[-2]+vector(0.5*drc["minwidth_metal1"],0))
|
||||
self.drain_connect_index = len(self.insts) - 1
|
||||
# Add each vertical segment
|
||||
for a in drain_positions:
|
||||
self.add_path(("metal1"), [a,a-pin_offset])
|
||||
# Add a single horizontal pin
|
||||
self.add_center_layout_pin_segment(text="D",
|
||||
layer="metal1",
|
||||
start=drain_positions[0]-pin_offset-end_offset,
|
||||
end=drain_positions[-1]-pin_offset+end_offset)
|
||||
|
||||
def add_poly(self):
|
||||
# left_most poly
|
||||
poly_xoffset = self.active_contact.via_layer_position.x \
|
||||
+ drc["minwidth_contact"] + drc["contact_to_poly"]
|
||||
poly_yoffset = -drc["poly_extend_active"]
|
||||
self.poly_positions = []
|
||||
# following poly(s)
|
||||
"""
|
||||
Add the poly gates(s) and (optionally) connect them.
|
||||
"""
|
||||
# poly is one contacted spacing from the end and down an extension
|
||||
poly_offset = vector(self.active_offset.x + self.end_to_poly + 0.5*self.poly_width,0.5*self.poly_height)
|
||||
|
||||
# poly_positions are the bottom center of the poly gates
|
||||
poly_positions = []
|
||||
|
||||
for i in range(0, self.mults):
|
||||
if self.connect_poly:
|
||||
# Add the rectangle in case we remove the pin when joining fingers
|
||||
self.add_rect(layer="poly",
|
||||
offset=[poly_xoffset, poly_yoffset],
|
||||
width=self.poly_width,
|
||||
height=self.poly_height)
|
||||
self.add_center_rect(layer="poly",
|
||||
offset=poly_offset,
|
||||
height=self.poly_height,
|
||||
width=self.poly_width)
|
||||
else:
|
||||
self.add_layout_pin(text="G",
|
||||
layer="poly",
|
||||
offset=[poly_xoffset, poly_yoffset],
|
||||
width=self.poly_width,
|
||||
height=self.poly_height)
|
||||
self.poly_positions.append(vector(poly_xoffset, poly_yoffset))
|
||||
poly_xoffset += self.mults_poly_to_poly + drc["minwidth_poly"]
|
||||
self.add_center_layout_pin_rect(text="G",
|
||||
layer="poly",
|
||||
offset=poly_offset,
|
||||
height=self.poly_height,
|
||||
width=self.poly_width)
|
||||
poly_positions.append(poly_offset)
|
||||
poly_offset = poly_offset + vector(self.poly_pitch,0)
|
||||
|
||||
if self.connect_poly:
|
||||
self.connect_fingered_poly(poly_positions)
|
||||
|
||||
def add_active(self):
|
||||
"""Adding the diffusion (active region = diffusion region)"""
|
||||
offset = self.active_position = [0, 0]
|
||||
"""
|
||||
Adding the diffusion (active region = diffusion region)
|
||||
"""
|
||||
self.add_rect(layer="active",
|
||||
offset=offset,
|
||||
offset=self.active_offset,
|
||||
width=self.active_width,
|
||||
height=self.active_height)
|
||||
|
||||
def add_implants(self):
|
||||
def add_well_implant(self):
|
||||
"""
|
||||
Add an (optional) well and implant for the type of transistor.
|
||||
"""
|
||||
if self.tx_type == "nmos":
|
||||
self.add_nmos_implants()
|
||||
implant_type = "n"
|
||||
well_type = "p"
|
||||
elif self.tx_type == "pmos":
|
||||
self.add_pmos_implants()
|
||||
|
||||
def add_nmos_implants(self):
|
||||
offset = self.pwell_position = [-drc["well_enclosure_active"], -drc["well_enclosure_active"]]
|
||||
if info["has_pwell"]:
|
||||
self.add_rect(layer="pwell",
|
||||
offset=offset,
|
||||
implant_type = "p"
|
||||
well_type = "n"
|
||||
else:
|
||||
self.error("Invalid transitor type.",-1)
|
||||
|
||||
if info["has_{}well".format(well_type)]:
|
||||
self.add_rect(layer="{}well".format(well_type),
|
||||
offset=(0,0),
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
self.add_rect(layer="vtg",
|
||||
offset=offset,
|
||||
offset=(0,0),
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
xlength = self.active_width
|
||||
ylength = self.active_height
|
||||
self.add_rect(layer="nimplant",
|
||||
offset=self.active_position,
|
||||
width=xlength,
|
||||
height=ylength)
|
||||
self.add_rect(layer="{}implant".format(implant_type),
|
||||
offset=self.active_offset,
|
||||
width=self.active_width,
|
||||
height=self.active_height)
|
||||
|
||||
def add_pmos_implants(self):
|
||||
offset = self.nwell_position = [-drc["well_enclosure_active"], -drc["well_enclosure_active"]]
|
||||
if info["has_nwell"]:
|
||||
self.add_rect(layer="nwell",
|
||||
offset=offset,
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
self.add_rect(layer="vtg",
|
||||
offset=offset,
|
||||
width=self.well_width,
|
||||
height=self.well_height)
|
||||
xlength = self.active_width
|
||||
ylength = self.active_height
|
||||
self.add_rect(layer="pimplant",
|
||||
offset=self.active_position,
|
||||
width=xlength,
|
||||
height=ylength)
|
||||
|
||||
def calculate_num_contacts(self):
|
||||
""" Calculates the possible number of source/drain contacts in a column """
|
||||
# Used for over-riding the contact dimensions
|
||||
possible_length = self.active_height - 2 * drc["active_extend_contact"]
|
||||
y = 1
|
||||
while True:
|
||||
temp_length = (y * drc["minwidth_contact"]) + ((y - 1) * drc["contact_to_contact"])
|
||||
if round(possible_length - temp_length, 6) < 0:
|
||||
return y - 1
|
||||
y += 1
|
||||
"""
|
||||
Calculates the possible number of source/drain contacts in a finger.
|
||||
For now, it is hard set as 1.
|
||||
"""
|
||||
return 1
|
||||
|
||||
|
||||
def get_contact_positions(self):
|
||||
"""
|
||||
Create a list of the centers of drain and source contact positions.
|
||||
"""
|
||||
# The first one will always be a source
|
||||
source_positions = [self.contact_offset]
|
||||
drain_positions = []
|
||||
|
||||
# For all but the last finger...
|
||||
for i in range(self.mults-1):
|
||||
if i%2:
|
||||
# It's a source... so offset from previous drain.
|
||||
source_positions.append(drain_positions[-1] + vector(self.contact_pitch,0))
|
||||
else:
|
||||
# It's a drain... so offset from previous source.
|
||||
drain_positions.append(source_positions[-1] + vector(self.contact_pitch,0))
|
||||
|
||||
# The last one will always be a drain
|
||||
drain_positions.append(source_positions[-1] + vector(self.contact_pitch,0))
|
||||
|
||||
return [source_positions,drain_positions]
|
||||
|
||||
def add_active_contacts(self):
|
||||
self.active_contact_positions = []
|
||||
"""
|
||||
Add the active contacts to the transistor.
|
||||
"""
|
||||
|
||||
# left_most contact column
|
||||
contact_xoffset = 0
|
||||
contact_yoffset = (self.active_height - self.active_contact.height) / 2
|
||||
offset = vector(contact_xoffset, contact_yoffset)
|
||||
contact=self.add_contact(layers=("active", "contact", "metal1"),
|
||||
offset=offset,
|
||||
size=(1, self.num_contacts))
|
||||
# source are even
|
||||
if self.connect_active:
|
||||
# Add this in case the pins get removed when fingers joined
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset+contact.second_layer_position,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
else:
|
||||
self.add_layout_pin(text="S",
|
||||
layer="metal1",
|
||||
offset=offset+contact.second_layer_position,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
self.active_contact_positions.append(offset)
|
||||
[source_positions,drain_positions] = self.get_contact_positions()
|
||||
|
||||
# middle contact columns
|
||||
for i in range(self.mults - 1):
|
||||
contact_xoffset = self.poly_positions[i].x + self.poly_width \
|
||||
+ (self.mults_poly_to_poly / 2) \
|
||||
- (drc["minwidth_contact"] / 2) - \
|
||||
self.active_contact.via_layer_position.x
|
||||
offset = vector(contact_xoffset, contact_yoffset)
|
||||
contact=self.add_contact(layers=("active", "contact", "metal1"),
|
||||
offset=offset,
|
||||
size=(1, self.num_contacts))
|
||||
# source are even, drain are odd
|
||||
for pos in source_positions:
|
||||
contact=self.add_center_contact(layers=("active", "contact", "metal1"),
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts))
|
||||
if self.connect_active:
|
||||
# Add this in case the pins get removed when fingers joined
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset+contact.second_layer_position,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
self.add_center_rect(layer="metal1",
|
||||
offset=pos,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
else:
|
||||
name = "S" if i%2==1 else "D"
|
||||
self.add_layout_pin(text=name,
|
||||
layer="metal1",
|
||||
offset=offset+contact.second_layer_position,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
self.add_center_layout_pin_rect(text="S",
|
||||
layer="metal1",
|
||||
offset=pos,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
|
||||
self.active_contact_positions.append(offset)
|
||||
|
||||
# right_most contact column
|
||||
contact_xoffset = self.poly_positions[-1].x \
|
||||
+ self.poly_width + drc["contact_to_poly"] - \
|
||||
self.active_contact.via_layer_position.x
|
||||
offset = vector(contact_xoffset, contact_yoffset)
|
||||
contact=self.add_contact(layers=("active", "contact", "metal1"),
|
||||
offset=offset,
|
||||
size=(1, self.num_contacts))
|
||||
# source are even, drain are odd
|
||||
|
||||
for pos in drain_positions:
|
||||
contact=self.add_center_contact(layers=("active", "contact", "metal1"),
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts))
|
||||
if self.connect_active:
|
||||
# Add this in case the pins get removed when fingers joined
|
||||
self.add_center_rect(layer="metal1",
|
||||
offset=pos,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
else:
|
||||
self.add_center_layout_pin_rect(text="D",
|
||||
layer="metal1",
|
||||
offset=pos,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
|
||||
if self.connect_active:
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset+contact.second_layer_position,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
else:
|
||||
name = "D" if self.mults%2==1 else "S"
|
||||
self.add_layout_pin(text=name,
|
||||
layer="metal1",
|
||||
offset=offset+contact.second_layer_position,
|
||||
width=contact.second_layer_width,
|
||||
height=contact.second_layer_height)
|
||||
# Add this in case the pins get removed when fingers joined
|
||||
self.active_contact_positions.append(offset)
|
||||
self.connect_fingered_active(drain_positions, source_positions)
|
||||
|
||||
|
||||
|
||||
# def remove_drain_connect(self):
|
||||
# debug.info(3,"Removing drain on {}".format(self.name))
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ def run_drc(name, gds_name):
|
|||
implemented in gds_name."""
|
||||
|
||||
debug.warning("DRC using magic not implemented.")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
# the runset file contains all the options to run drc
|
||||
from tech import drc
|
||||
|
|
@ -139,7 +139,7 @@ def run_lvs(name, gds_name, sp_name):
|
|||
implemented in gds_name and sp_name. """
|
||||
|
||||
debug.warning("LVS using magic+netgen not implemented.")
|
||||
return 0
|
||||
return 1
|
||||
|
||||
from tech import drc
|
||||
lvs_rules = drc["lvs_rules"]
|
||||
|
|
|
|||
|
|
@ -141,9 +141,9 @@ class wordline_driver(design.design):
|
|||
a_pin = inv1_inst.get_pin("A")
|
||||
a_pos = a_pin.lc()
|
||||
clk_offset = vector(en_pin.bc().x,a_pos.y)
|
||||
self.add_center_rect(layer="metal1",
|
||||
start=clk_offset,
|
||||
end=a_pos)
|
||||
self.add_center_segment(layer="metal1",
|
||||
start=clk_offset,
|
||||
end=a_pos)
|
||||
m1m2_via = self.add_center_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=clk_offset)
|
||||
|
||||
|
|
@ -167,17 +167,17 @@ class wordline_driver(design.design):
|
|||
input_offset = vector(0,a_pos.y)
|
||||
mid_via_offset = vector(clk_offset.x,a_pos.y) + vector(0.5*drc["minwidth_metal2"]+drc["metal2_to_metal2"]+0.5*m1m2_via.width,0)
|
||||
# must under the clk line in M1
|
||||
self.add_center_layout_pin(text="in[{0}]".format(row),
|
||||
layer="metal1",
|
||||
start=input_offset,
|
||||
end=mid_via_offset)
|
||||
self.add_center_layout_pin_segment(text="in[{0}]".format(row),
|
||||
layer="metal1",
|
||||
start=input_offset,
|
||||
end=mid_via_offset)
|
||||
self.add_center_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=mid_via_offset)
|
||||
|
||||
# now connect to the nand2 A
|
||||
self.add_center_rect(layer="metal2",
|
||||
start=mid_via_offset,
|
||||
end=a_pos)
|
||||
self.add_center_segment(layer="metal2",
|
||||
start=mid_via_offset,
|
||||
end=a_pos)
|
||||
self.add_center_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=a_pos + vector(0.5*m1m2_via.height,0),
|
||||
rotate=90)
|
||||
|
|
@ -185,10 +185,10 @@ class wordline_driver(design.design):
|
|||
|
||||
# output each WL on the right
|
||||
wl_offset = inv2_inst.get_pin("Z").rc()
|
||||
self.add_center_layout_pin(text="wl[{0}]".format(row),
|
||||
layer="metal1",
|
||||
start=wl_offset,
|
||||
end=wl_offset-vector(drc["minwidth_metal1"],0))
|
||||
self.add_center_layout_pin_segment(text="wl[{0}]".format(row),
|
||||
layer="metal1",
|
||||
start=wl_offset,
|
||||
end=wl_offset-vector(drc["minwidth_metal1"],0))
|
||||
|
||||
|
||||
def analytical_delay(self, slew, load=0):
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ drc["active_to_active"] = 0.9
|
|||
drc["well_enclosure_active"] = 1.8
|
||||
drc["well_extend_active"] = 1.8
|
||||
|
||||
#Implant (None in IBM)
|
||||
#Implant
|
||||
drc["implant_to_gate"] = 0
|
||||
drc["implant_to_channel"] = 0
|
||||
drc["implant_to_contact"] = 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue