mirror of https://github.com/VLSIDA/OpenRAM.git
Fix bug for even number of fingers. Add even finger tests.
This commit is contained in:
parent
7ff82a2aed
commit
d4f8d63442
137
compiler/ptx.py
137
compiler/ptx.py
|
|
@ -57,52 +57,6 @@ class ptx(design.design):
|
||||||
self.add_poly()
|
self.add_poly()
|
||||||
self.add_active_contacts()
|
self.add_active_contacts()
|
||||||
|
|
||||||
# offset this BEFORE we add the active/poly connections
|
|
||||||
#self.offset_all_coordinates()
|
|
||||||
|
|
||||||
def offset_attributes(self, coordinate):
|
|
||||||
"""Translates all stored 2d cartesian coordinates within the
|
|
||||||
attr dictionary"""
|
|
||||||
# FIXME: This is dangerous. I think we should not do this, but explicitly
|
|
||||||
# offset the necessary coordinates. It is only used in ptx for now!
|
|
||||||
|
|
||||||
for attr_key in dir(self):
|
|
||||||
attr_val = getattr(self,attr_key)
|
|
||||||
|
|
||||||
# skip the list of things as these will be offset separately
|
|
||||||
if (attr_key in ['objs','insts','mods','pins','conns','name_map']): continue
|
|
||||||
|
|
||||||
# if is a list
|
|
||||||
if isinstance(attr_val, list):
|
|
||||||
|
|
||||||
for i in range(len(attr_val)):
|
|
||||||
# each unit in the list is a list coordinates
|
|
||||||
if isinstance(attr_val[i], (list,vector)):
|
|
||||||
attr_val[i] = vector(attr_val[i] - coordinate)
|
|
||||||
# the list itself is a coordinate
|
|
||||||
else:
|
|
||||||
if len(attr_val)!=2: continue
|
|
||||||
for val in attr_val:
|
|
||||||
if not isinstance(val, (int, long, float)): continue
|
|
||||||
setattr(self,attr_key, vector(attr_val - coordinate))
|
|
||||||
break
|
|
||||||
|
|
||||||
# if is a vector coordinate
|
|
||||||
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)
|
|
||||||
|
|
||||||
# # 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):
|
def create_spice(self):
|
||||||
self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
|
self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
|
||||||
" ".join(self.pins)))
|
" ".join(self.pins)))
|
||||||
|
|
@ -201,55 +155,6 @@ class ptx(design.design):
|
||||||
height=drc["minwidth_poly"])
|
height=drc["minwidth_poly"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def pairwise(self, iterable):
|
|
||||||
"""
|
|
||||||
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_source_wire(self):
|
|
||||||
self.source_positions = []
|
|
||||||
source_contact_pos = self.active_contact_positions[0:][::2] # even indexes
|
|
||||||
for a, b in self.pairwise(source_contact_pos):
|
|
||||||
correct=vector(0.5 * (self.active_contact.width -
|
|
||||||
drc["minwidth_metal1"] + drc["minwidth_contact"]),
|
|
||||||
0.5 * (self.active_contact.height - drc["minwidth_contact"])
|
|
||||||
- drc["metal1_extend_contact"])
|
|
||||||
connected=vector(b.x + drc["minwidth_metal1"],
|
|
||||||
a.y + self.active_contact.height + drc["metal1_to_metal1"])
|
|
||||||
self.source_positions.append(a + correct)
|
|
||||||
self.source_positions.append(vector(a.x + correct.x, connected.y))
|
|
||||||
self.source_positions.append(vector(b.x + correct.x,
|
|
||||||
connected.y + 0.5 * drc["minwidth_metal2"]))
|
|
||||||
self.source_positions.append(b + correct)
|
|
||||||
|
|
||||||
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"]),
|
|
||||||
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"])
|
|
||||||
self.drain_positions.append(vector(c + correct))
|
|
||||||
self.drain_positions.append(vector(c.x + correct.x, connected.y))
|
|
||||||
self.drain_positions.append(vector(d.x + correct.x,
|
|
||||||
connected.y - 0.5 * drc["minwidth_metal1"]))
|
|
||||||
self.drain_positions.append(vector(d + correct))
|
|
||||||
|
|
||||||
return drain_positions
|
|
||||||
|
|
||||||
|
|
||||||
def connect_fingered_active(self, drain_positions, source_positions):
|
def connect_fingered_active(self, drain_positions, source_positions):
|
||||||
"""
|
"""
|
||||||
Connect each contact up/down to a source or drain pin
|
Connect each contact up/down to a source or drain pin
|
||||||
|
|
@ -366,8 +271,7 @@ class ptx(design.design):
|
||||||
source_positions = [self.contact_offset]
|
source_positions = [self.contact_offset]
|
||||||
drain_positions = []
|
drain_positions = []
|
||||||
|
|
||||||
# For all but the last finger...
|
for i in range(self.mults):
|
||||||
for i in range(self.mults-1):
|
|
||||||
if i%2:
|
if i%2:
|
||||||
# It's a source... so offset from previous drain.
|
# It's a source... so offset from previous drain.
|
||||||
source_positions.append(drain_positions[-1] + vector(self.contact_pitch,0))
|
source_positions.append(drain_positions[-1] + vector(self.contact_pitch,0))
|
||||||
|
|
@ -375,9 +279,6 @@ class ptx(design.design):
|
||||||
# It's a drain... so offset from previous source.
|
# It's a drain... so offset from previous source.
|
||||||
drain_positions.append(source_positions[-1] + vector(self.contact_pitch,0))
|
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]
|
return [source_positions,drain_positions]
|
||||||
|
|
||||||
def add_active_contacts(self):
|
def add_active_contacts(self):
|
||||||
|
|
@ -426,39 +327,3 @@ class ptx(design.design):
|
||||||
self.connect_fingered_active(drain_positions, source_positions)
|
self.connect_fingered_active(drain_positions, source_positions)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# def remove_drain_connect(self):
|
|
||||||
# debug.info(3,"Removing drain on {}".format(self.name))
|
|
||||||
# # FIXME: This is horrible exception handling!
|
|
||||||
# try:
|
|
||||||
# del self.insts[self.drain_connect_index]
|
|
||||||
# del self.drain_connect_index
|
|
||||||
# self.offset_all_coordinates()
|
|
||||||
# # change the name so it is unique
|
|
||||||
# self.name = self.name + "_rd"
|
|
||||||
# except:
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# def remove_source_connect(self):
|
|
||||||
# debug.info(3,"Removing source on {}".format(self.name))
|
|
||||||
# # FIXME: This is horrible exception handling!
|
|
||||||
# try:
|
|
||||||
# del self.insts[self.source_connect_index]
|
|
||||||
# del self.source_connect_index
|
|
||||||
# if isinstance(self.drain_connect_index, int):
|
|
||||||
# self.drain_connect_index -= 1
|
|
||||||
# self.offset_all_coordinates()
|
|
||||||
# # change the name so it is unique
|
|
||||||
# self.name = self.name + "_rs"
|
|
||||||
# except:
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# def remove_poly_connect(self):
|
|
||||||
# # FIXME: This is horrible exception handling!
|
|
||||||
# try:
|
|
||||||
# del self.objs[self.poly_connect_index]
|
|
||||||
# self.offset_all_coordinates()
|
|
||||||
# # change the name so it is unique
|
|
||||||
# self.name = self.name + "_rp"
|
|
||||||
# except:
|
|
||||||
# pass
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
"Run a regresion test on a basic parameterized transistors"
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
import debug
|
||||||
|
import verify
|
||||||
|
|
||||||
|
OPTS = globals.OPTS
|
||||||
|
|
||||||
|
#@unittest.skip("SKIPPING 03_ptx_test")
|
||||||
|
|
||||||
|
|
||||||
|
class ptx_test(unittest.TestCase):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
# we will manually run lvs/drc
|
||||||
|
OPTS.check_lvsdrc = False
|
||||||
|
|
||||||
|
import ptx
|
||||||
|
import tech
|
||||||
|
|
||||||
|
debug.info(2, "Checking three fingers NMOS")
|
||||||
|
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||||
|
mults=4,
|
||||||
|
tx_type="nmos",
|
||||||
|
connect_active=True,
|
||||||
|
connect_poly=True)
|
||||||
|
self.local_check(fet)
|
||||||
|
|
||||||
|
OPTS.check_lvsdrc = True
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
def add_mods(self, fet):
|
||||||
|
self.create_contacts()
|
||||||
|
self.add_well_extension(fet)
|
||||||
|
self.add_wire_extension(fet)
|
||||||
|
self.add_well_tiedown(fet)
|
||||||
|
self.add_poly_tiedown(fet)
|
||||||
|
|
||||||
|
def create_contacts(self):
|
||||||
|
layer_stack = ("active", "contact", "metal1")
|
||||||
|
self.well_contact = contact.contact(layer_stack)
|
||||||
|
|
||||||
|
layer_stack = ("poly", "contact", "metal1")
|
||||||
|
self.poly_contact = contact.contact(layer_stack)
|
||||||
|
|
||||||
|
def add_well_tiedown(self, fet):
|
||||||
|
offset = [fet.active_contact_positions[0][0],
|
||||||
|
fet.active_contact_positions[0][1] + fet.well_height]
|
||||||
|
fet.add_inst(name="well_tap",
|
||||||
|
mod=self.well_contact,
|
||||||
|
offset=offset,
|
||||||
|
mirror="R0",
|
||||||
|
rotate=0)
|
||||||
|
fet.well_contact = self.well_contact
|
||||||
|
fet.well_tiedown_location = offset
|
||||||
|
|
||||||
|
def add_well_extension(self, fet):
|
||||||
|
well_define = {"pmos": "nwell",
|
||||||
|
"nmos": "pwell"}
|
||||||
|
well_type = well_define[fet.tx_type]
|
||||||
|
offset = getattr(fet,"{}_position".format(well_type))
|
||||||
|
if tech.info["has_{0}".format(well_type)]:
|
||||||
|
fet.add_rect(layerNumber=tech.layer[well_type],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=2 * fet.well_height)
|
||||||
|
fet.add_rect(layerNumber=tech.layer["{0}implant".format(fet.tx_type[0])],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=2 * fet.well_height)
|
||||||
|
fet.add_rect(layerNumber=tech.layer["vtg"],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=2 * fet.well_height)
|
||||||
|
|
||||||
|
well_type = "{0}well".format(fet.tx_type[0])
|
||||||
|
offset[1] = offset[1] - 3 * fet.well_height
|
||||||
|
if tech.info["has_{0}".format(well_type)]:
|
||||||
|
fet.add_rect(layerNumber=tech.layer[well_type],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=3 * fet.well_height)
|
||||||
|
fet.add_rect(layerNumber=tech.layer["{0}implant".format(well_define[fet.tx_type][
|
||||||
|
0])],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=3 * fet.well_height)
|
||||||
|
fet.add_rect(layerNumber=tech.layer["vtg"],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=3 * fet.well_height)
|
||||||
|
|
||||||
|
def add_wire_extension(self, fet):
|
||||||
|
xcorrect = (fet.active_contact.width / 2) - (tech.drc["minwidth_metal1"] / 2)
|
||||||
|
offset = [fet.active_contact_positions[0][0] + xcorrect,
|
||||||
|
fet.active_contact_positions[0][1]]
|
||||||
|
fet.add_rect(layerNumber=tech.layer["metal1"],
|
||||||
|
offset=offset,
|
||||||
|
width=tech.drc["minwidth_metal1"],
|
||||||
|
height=fet.well_height)
|
||||||
|
|
||||||
|
offset = [fet.active_contact_positions[-1][0] + xcorrect,
|
||||||
|
fet.active_contact_positions[-1][1] - 2 * fet.well_height]
|
||||||
|
fet.add_rect(layerNumber=tech.layer["metal1"],
|
||||||
|
offset=offset,
|
||||||
|
width=tech.drc["minwidth_metal1"],
|
||||||
|
height=2 * fet.well_height)
|
||||||
|
|
||||||
|
offset = [fet.poly_positions[-1][0],
|
||||||
|
fet.poly_positions[-1][1] - (fet.well_height)]
|
||||||
|
fet.add_rect(layerNumber=tech.layer["poly"],
|
||||||
|
offset=offset,
|
||||||
|
width=tech.drc["minwidth_poly"],
|
||||||
|
height=fet.well_height)
|
||||||
|
|
||||||
|
def add_poly_tiedown(self, fet):
|
||||||
|
xcorrect = abs(self.poly_contact.upper_layer_vertical_enclosure -
|
||||||
|
self.poly_contact.lower_layer_vertical_enclosure)
|
||||||
|
offset = [fet.poly_positions[-1][0] - xcorrect,
|
||||||
|
fet.poly_positions[-1][1] - (fet.well_height)]
|
||||||
|
fet.add_inst(name="poly_contact",
|
||||||
|
mod=self.poly_contact,
|
||||||
|
offset=offset,
|
||||||
|
mirror="R270")
|
||||||
|
|
||||||
|
|
||||||
|
offset = [fet.active_contact_positions[-1][0], fet.active_contact_positions
|
||||||
|
[-1][1] - 2 * fet.well_height - self.well_contact.height]
|
||||||
|
fet.poly_tiedown_location = offset
|
||||||
|
fet.add_inst(name="n_tiedown",
|
||||||
|
mod=self.well_contact,
|
||||||
|
offset=offset)
|
||||||
|
tech.ptx_port.add_custom_layer(fet)
|
||||||
|
|
||||||
|
def local_check(self, fet):
|
||||||
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
|
tempgds = OPTS.openram_temp + "temp.gds"
|
||||||
|
|
||||||
|
fet.sp_write(tempspice)
|
||||||
|
fet.gds_write(tempgds)
|
||||||
|
|
||||||
|
self.assertFalse(verify.run_drc(fet.name, tempgds))
|
||||||
|
|
||||||
|
os.remove(tempspice)
|
||||||
|
os.remove(tempgds)
|
||||||
|
|
||||||
|
# instantiate a copy of the class to actually run the test
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
"Run a regresion test on a basic parameterized transistors"
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
import debug
|
||||||
|
import verify
|
||||||
|
|
||||||
|
OPTS = globals.OPTS
|
||||||
|
|
||||||
|
#@unittest.skip("SKIPPING 03_ptx_test")
|
||||||
|
|
||||||
|
|
||||||
|
class ptx_test(unittest.TestCase):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
# we will manually run lvs/drc
|
||||||
|
OPTS.check_lvsdrc = False
|
||||||
|
|
||||||
|
import ptx
|
||||||
|
import tech
|
||||||
|
|
||||||
|
debug.info(2, "Checking three fingers PMOS")
|
||||||
|
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||||
|
mults=4,
|
||||||
|
tx_type="pmos",
|
||||||
|
connect_active=True,
|
||||||
|
connect_poly=True)
|
||||||
|
self.local_check(fet)
|
||||||
|
|
||||||
|
OPTS.check_lvsdrc = True
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
def add_mods(self, fet):
|
||||||
|
self.create_contacts()
|
||||||
|
self.add_well_extension(fet)
|
||||||
|
self.add_wire_extension(fet)
|
||||||
|
self.add_well_tiedown(fet)
|
||||||
|
self.add_poly_tiedown(fet)
|
||||||
|
|
||||||
|
def create_contacts(self):
|
||||||
|
layer_stack = ("active", "contact", "metal1")
|
||||||
|
self.well_contact = contact.contact(layer_stack)
|
||||||
|
|
||||||
|
layer_stack = ("poly", "contact", "metal1")
|
||||||
|
self.poly_contact = contact.contact(layer_stack)
|
||||||
|
|
||||||
|
def add_well_tiedown(self, fet):
|
||||||
|
offset = [fet.active_contact_positions[0][0],
|
||||||
|
fet.active_contact_positions[0][1] + fet.well_height]
|
||||||
|
fet.add_inst(name="well_tap",
|
||||||
|
mod=self.well_contact,
|
||||||
|
offset=offset,
|
||||||
|
mirror="R0",
|
||||||
|
rotate=0)
|
||||||
|
fet.well_contact = self.well_contact
|
||||||
|
fet.well_tiedown_location = offset
|
||||||
|
|
||||||
|
def add_well_extension(self, fet):
|
||||||
|
well_define = {"pmos": "nwell",
|
||||||
|
"nmos": "pwell"}
|
||||||
|
well_type = well_define[fet.tx_type]
|
||||||
|
offset = getattr(fet,"{}_position".format(well_type))
|
||||||
|
if tech.info["has_{0}".format(well_type)]:
|
||||||
|
fet.add_rect(layerNumber=tech.layer[well_type],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=2 * fet.well_height)
|
||||||
|
fet.add_rect(layerNumber=tech.layer["{0}implant".format(fet.tx_type[0])],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=2 * fet.well_height)
|
||||||
|
fet.add_rect(layerNumber=tech.layer["vtg"],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=2 * fet.well_height)
|
||||||
|
|
||||||
|
well_type = "{0}well".format(fet.tx_type[0])
|
||||||
|
offset[1] = offset[1] - 3 * fet.well_height
|
||||||
|
if tech.info["has_{0}".format(well_type)]:
|
||||||
|
fet.add_rect(layerNumber=tech.layer[well_type],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=3 * fet.well_height)
|
||||||
|
fet.add_rect(layerNumber=tech.layer["{0}implant".format(well_define[fet.tx_type][
|
||||||
|
0])],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=3 * fet.well_height)
|
||||||
|
fet.add_rect(layerNumber=tech.layer["vtg"],
|
||||||
|
offset=offset,
|
||||||
|
width=fet.well_width,
|
||||||
|
height=3 * fet.well_height)
|
||||||
|
|
||||||
|
def add_wire_extension(self, fet):
|
||||||
|
xcorrect = (fet.active_contact.width / 2) - (tech.drc["minwidth_metal1"] / 2)
|
||||||
|
offset = [fet.active_contact_positions[0][0] + xcorrect,
|
||||||
|
fet.active_contact_positions[0][1]]
|
||||||
|
fet.add_rect(layerNumber=tech.layer["metal1"],
|
||||||
|
offset=offset,
|
||||||
|
width=tech.drc["minwidth_metal1"],
|
||||||
|
height=fet.well_height)
|
||||||
|
|
||||||
|
offset = [fet.active_contact_positions[-1][0] + xcorrect,
|
||||||
|
fet.active_contact_positions[-1][1] - 2 * fet.well_height]
|
||||||
|
fet.add_rect(layerNumber=tech.layer["metal1"],
|
||||||
|
offset=offset,
|
||||||
|
width=tech.drc["minwidth_metal1"],
|
||||||
|
height=2 * fet.well_height)
|
||||||
|
|
||||||
|
offset = [fet.poly_positions[-1][0],
|
||||||
|
fet.poly_positions[-1][1] - (fet.well_height)]
|
||||||
|
fet.add_rect(layerNumber=tech.layer["poly"],
|
||||||
|
offset=offset,
|
||||||
|
width=tech.drc["minwidth_poly"],
|
||||||
|
height=fet.well_height)
|
||||||
|
|
||||||
|
def add_poly_tiedown(self, fet):
|
||||||
|
xcorrect = abs(self.poly_contact.upper_layer_vertical_enclosure -
|
||||||
|
self.poly_contact.lower_layer_vertical_enclosure)
|
||||||
|
offset = [fet.poly_positions[-1][0] - xcorrect,
|
||||||
|
fet.poly_positions[-1][1] - (fet.well_height)]
|
||||||
|
fet.add_inst(name="poly_contact",
|
||||||
|
mod=self.poly_contact,
|
||||||
|
offset=offset,
|
||||||
|
mirror="R270")
|
||||||
|
|
||||||
|
|
||||||
|
offset = [fet.active_contact_positions[-1][0], fet.active_contact_positions
|
||||||
|
[-1][1] - 2 * fet.well_height - self.well_contact.height]
|
||||||
|
fet.poly_tiedown_location = offset
|
||||||
|
fet.add_inst(name="n_tiedown",
|
||||||
|
mod=self.well_contact,
|
||||||
|
offset=offset)
|
||||||
|
tech.ptx_port.add_custom_layer(fet)
|
||||||
|
|
||||||
|
def local_check(self, fet):
|
||||||
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
|
tempgds = OPTS.openram_temp + "temp.gds"
|
||||||
|
|
||||||
|
fet.sp_write(tempspice)
|
||||||
|
fet.gds_write(tempgds)
|
||||||
|
|
||||||
|
self.assertFalse(verify.run_drc(fet.name, tempgds))
|
||||||
|
|
||||||
|
os.remove(tempspice)
|
||||||
|
os.remove(tempgds)
|
||||||
|
|
||||||
|
# instantiate a copy of the class to actually run the test
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
Loading…
Reference in New Issue