Add separate well design rules.

Needed to fix various pgates with wells.
Did some cleanup of these gates as well.
This commit is contained in:
mrg 2020-01-23 19:43:41 +00:00
parent 306740f0f3
commit 9beb0f4ece
16 changed files with 181 additions and 111 deletions

View File

@ -68,8 +68,10 @@ class layout():
return (base_offset, y_dir)
def find_lowest_coords(self):
"""Finds the lowest set of 2d cartesian coordinates within
this layout"""
"""
Finds the lowest set of 2d cartesian coordinates within
this layout
"""
if len(self.objs) > 0:
lowestx1 = min(obj.lx() for obj in self.objs if obj.name != "label")
@ -116,6 +118,54 @@ class layout():
return vector(max(highestx1, highestx2),
max(highesty1, highesty2))
def find_highest_layer_coords(self, layer):
"""
Finds the highest set of 2d cartesian coordinates within
this layout on a layer
"""
# Only consider the layer not the purpose for now
layerNumber = techlayer[layer][0]
try:
highestx = max(obj.rx() for obj in self.objs if obj.layerNumber == layerNumber)
except ValueError:
highestx =0
try:
highesty = max(obj.uy() for obj in self.objs if obj.layerNumber == layerNumber)
except ValueError:
highesty = 0
for inst in self.insts:
# This really should be rotated/mirrored etc...
subcoord = inst.mod.find_highest_layer_coords(layer) + inst.offset
highestx = max(highestx, subcoord.x)
highesty = max(highesty, subcoord.y)
return vector(highestx, highesty)
def find_lowest_layer_coords(self, layer):
"""
Finds the highest set of 2d cartesian coordinates within
this layout on a layer
"""
# Only consider the layer not the purpose for now
layerNumber = techlayer[layer][0]
try:
lowestx = min(obj.lx() for obj in self.objs if obj.layerNumber == layerNumber)
except ValueError:
lowestx = 0
try:
lowesty = min(obj.by() for obj in self.objs if obj.layerNumber == layerNumber)
except ValueError:
lowesty = 0
for inst in self.insts:
# This really should be rotated/mirrored etc...
subcoord = inst.mod.find_lowest_layer_coords(layer) + inst.offset
lowestx = min(lowestx, subcoord.x)
lowesty = min(lowesty, subcoord.y)
return vector(lowestx, lowesty)
def translate_all(self, offset):
"""
Translates all objects, instances, and pins by the given (x,y) offset
@ -429,7 +479,7 @@ class layout():
return inst
def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None):
"""
"""
Add a three layer via structure by the center coordinate
accounting for mirroring and rotation.
"""
@ -466,7 +516,7 @@ class layout():
mults=mults,
tx_type=tx_type)
self.add_mod(mos)
inst = self.add_inst(name=mos.name,
inst = self.add_inst(name=mos.name,
mod=mos,
offset=offset,
mirror=mirror,
@ -652,22 +702,21 @@ class layout():
vertical=False,
make_pins=False)
def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins):
"""
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.
The other coordinate is a 0 since the bus provides a range.
TODO: combine with channel router.
"""
# 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)]
line_positions = {}
if vertical:
for i in range(len(names)):
line_offset = offset + vector(i*pitch,0)
line_offset = offset + vector(i * pitch, 0)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
@ -885,7 +934,7 @@ class layout():
return g
def vcg_nets_overlap(net1, net2, vertical, pitch):
"""
"""
Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps.
"""

View File

@ -977,12 +977,12 @@ class pbitcell(bitcell_base.bitcell_base):
"""
# extend pwell to encompass entire nmos region of the cell up to the
# height of the tallest nmos transistor
max_nmos_well_height = max(self.inverter_nmos.cell_well_height,
self.readwrite_nmos.cell_well_height,
self.write_nmos.cell_well_height,
self.read_nmos.cell_well_height)
max_nmos_well_height = max(self.inverter_nmos.well_height,
self.readwrite_nmos.well_height,
self.write_nmos.well_height,
self.read_nmos.well_height)
well_height = max_nmos_well_height + self.port_ypos \
- self.well_enclose_active - self.gnd_position.y
- self.nwell_enclose_active - self.gnd_position.y
offset = vector(self.leftmost_xpos, self.botmost_ypos)
self.add_rect(layer="pwell",
offset=offset,
@ -992,16 +992,16 @@ class pbitcell(bitcell_base.bitcell_base):
# extend nwell to encompass inverter_pmos
# calculate offset of the left pmos well
inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
- self.well_enclose_active
- self.nwell_enclose_active
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \
+ self.inverter_gap - self.well_enclose_active
+ self.inverter_gap - self.nwell_enclose_active
# calculate width of the two combined nwells
# calculate height to encompass nimplant connected to vdd
well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
+ 2 * self.well_enclose_active
+ 2 * self.nwell_enclose_active
well_height = self.vdd_position.y - inverter_well_ypos \
+ self.well_enclose_active + drc["minwidth_tx"]
+ self.nwell_enclose_active + drc["minwidth_tx"]
offset = [inverter_well_xpos, inverter_well_ypos]
self.add_rect(layer="nwell",

View File

@ -333,7 +333,7 @@ class bank(design.design):
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
# A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"),
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)

View File

@ -149,7 +149,7 @@ class port_address(design.design):
"""
# A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"),
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)
row_decoder_offset = vector(0,0)

View File

@ -212,7 +212,7 @@ class port_data(design.design):
# A space for wells or jogging m2 between modules
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"),
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)

View File

@ -8,7 +8,7 @@
import contact
import design
import debug
from tech import layer, drc
from tech import layer
from vector import vector
from globals import OPTS
from sram_factory import factory
@ -127,9 +127,10 @@ class pgate(design.design):
""" Extend the n/p wells to cover whole cell """
# Add a rail width to extend the well to the top of the rail
max_y_offset = self.height + 0.5 * self.m1_width
self.nwell_position = middle_position
nwell_height = max_y_offset - middle_position.y
nwell_max_offset = max(self.find_highest_layer_coords("nwell").y,
self.height + 0.5 * self.m1_width)
nwell_position = middle_position
nwell_height = nwell_max_offset - middle_position.y
if "nwell" in layer:
self.add_rect(layer="nwell",
offset=middle_position,
@ -137,11 +138,14 @@ class pgate(design.design):
height=nwell_height)
if "vtg" in layer:
self.add_rect(layer="vtg",
offset=self.nwell_position,
offset=nwell_position,
width=self.well_width,
height=nwell_height)
pwell_position = vector(0, -0.5 * self.m1_width)
# Start this half a rail width below the cell
pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y,
-0.5 * self.m1_width)
pwell_position = vector(0, pwell_min_offset)
pwell_height = middle_position.y - pwell_position.y
if "pwell" in layer:
self.add_rect(layer="pwell",
@ -168,7 +172,7 @@ class pgate(design.design):
# OR align the active with the top of PMOS active.
max_y_offset = self.height + 0.5 * self.m1_width
contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height,
max_y_offset - pmos.active_contact.first_layer_height / 2 - self.well_enclose_active)
max_y_offset - pmos.active_contact.first_layer_height / 2 - self.nwell_enclose_active)
contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact in x and y
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
@ -220,7 +224,7 @@ class pgate(design.design):
# Must be at least an well enclosure of active up
# from the bottom of the well
contact_yoffset = max(nmos_pos.y,
self.well_enclose_active \
self.nwell_enclose_active \
- nmos.active_contact.first_layer_height / 2)
contact_offset = vector(contact_xoffset, contact_yoffset)

View File

@ -153,7 +153,7 @@ class pinv(pgate.pgate):
# the well width is determined the multi-finger PMOS device width plus
# the well contact width and half well enclosure on both sides
self.well_width = self.pmos.active_width + self.pmos.active_contact.width \
+ self.active_space + 2*self.well_enclose_active
+ self.active_space + 2*self.nwell_enclose_active
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
@ -223,7 +223,7 @@ class pinv(pgate.pgate):
self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y))
# This will help with the wells
self.well_pos = vector(0, self.nmos_inst.uy())
self.well_pos = self.output_pos
def route_outputs(self):
"""

View File

@ -100,7 +100,7 @@ class pnand2(pgate.pgate):
# Enclosure space on the sides.
self.well_width = 2 * self.pmos.active_width + contact.activem1.width \
+ 2 * self.active_space \
+ 2 * self.well_enclose_active
+ 2 * self.nwell_enclose_active
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
@ -171,7 +171,7 @@ class pnand2(pgate.pgate):
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
self.well_pos = self.output_pos
def add_well_contacts(self):
"""

View File

@ -92,14 +92,11 @@ class pnand3(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
+ 2 * self.active_space + 2 * self.well_enclose_active \
+ 2 * self.active_space + 2 * self.nwell_enclose_active \
- self.overlap_offset.x
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This will help with the wells and the input/output placement
self.output_pos = vector(0, 0.5*self.height)
# This is the extra space needed to ensure DRC rules
# to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos")
@ -178,9 +175,12 @@ class pnand3(pgate.pgate):
self.nmos3_pos = nmos2_pos + self.overlap_offset
self.nmos3_inst.place(self.nmos3_pos)
# This will help with the wells and the input/output placement
self.output_pos = vector(0, 0.5*self.height)
# This should be placed at the top of the NMOS well
self.well_pos = vector(0, self.nmos1_inst.uy())
self.well_pos = self.output_pos
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """

View File

@ -98,7 +98,7 @@ class pnor2(pgate.pgate):
self.well_width = 2 * self.pmos.active_width \
+ self.pmos.active_contact.width \
+ 2 * self.active_space \
+ 2 * self.well_enclose_active
+ 2 * self.nwell_enclose_active
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
@ -136,7 +136,6 @@ class pnor2(pgate.pgate):
mod=self.pmos)
self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
@ -170,7 +169,7 @@ class pnor2(pgate.pgate):
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
self.well_pos = self.output_pos
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """

View File

@ -116,7 +116,7 @@ class precharge(design.design):
# adds the lower pmos to layout
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
self.well_enclose_active),
self.nwell_enclose_active),
self.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position)
@ -176,7 +176,7 @@ class precharge(design.design):
# adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.activem1.height / 2 \
+ self.well_extend_active)
+ self.nwell_extend_active)
self.add_via_center(layers=self.active_stack,
offset=well_contact_pos,
implant_type="n",

View File

@ -44,7 +44,7 @@ class ptristate_inv(pgate.pgate):
""" Calls all functions related to the generation of the netlist """
self.add_pins()
self.add_ptx()
self.create_ptx()
self.create_ptx()
def create_layout(self):
""" Calls all functions related to the generation of the layout """
@ -61,7 +61,6 @@ class ptristate_inv(pgate.pgate):
""" Adds pins for spice netlist """
self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"])
def setup_layout_constants(self):
"""
Pre-compute some handy layout parameters.
@ -73,7 +72,7 @@ class ptristate_inv(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2 * self.pmos.active_width + self.well_enclose_active
self.well_width = 2 * self.pmos.active_width + self.nwell_enclose_active
# Add an extra space because we route the output on the right of the S/D
self.width = self.well_width + 0.5 * self.m1_space
@ -81,7 +80,6 @@ class ptristate_inv(pgate.pgate):
# Make sure we can put a well above and below
self.top_bottom_space = max(contact.activem1.width, contact.activem1.height)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
@ -95,7 +93,6 @@ class ptristate_inv(pgate.pgate):
width=self.pmos_width,
mults=1,
tx_type="pmos")
self.add_mod(self.pmos)
def route_supply_rails(self):

View File

@ -58,9 +58,9 @@ class ptx(design.design):
# some transistor sizes in other netlist depend on pbitcell
self.create_layout()
#ll = self.find_lowest_coords()
#ur = self.find_highest_coords()
#self.add_boundary(ll, ur)
ll = self.find_lowest_coords()
ur = self.find_highest_coords()
self.add_boundary(ll, ur)
# (0,0) will be the corner of the active area (not the larger well)
self.translate_all(self.active_offset)
@ -99,7 +99,7 @@ class ptx(design.design):
drc("minwidth_poly"))
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd)
self.spice_device= main_str + area_str
self.spice_device = main_str + area_str
self.spice.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name))
@ -124,30 +124,30 @@ class ptx(design.design):
# This is not actually instantiated but used for calculations
self.active_contact = factory.create(module_type="contact",
layer_stack=self.active_stack,
directions = ("V", "V"),
directions=("V", "V"),
dimensions=(1, self.num_contacts))
# The contacted poly pitch (or uncontacted in an odd technology)
# The contacted poly pitch
self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width,
self.poly_space)
# The contacted poly pitch (or uncontacted in an odd technology)
self.contact_pitch = 2 * self.contact_to_gate + \
self.contact_width + self.poly_width
# The contacted poly pitch
self.contact_spacing = 2 * self.contact_to_gate + \
self.contact_width + self.poly_width
# This is measured because of asymmetric enclosure rules
active_enclose_contact = 0.5*(self.active_contact.width - self.contact_width)
# The enclosure of an active contact. Not sure about second term.
active_enclose_contact = max(self.active_enclose_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 + self.contact_to_gate
# This is the distance from the side of
# poly gate to the contacted end of active
# (i.e. the "outside" contacted diffusion sizes)
self.end_to_poly = self.active_contact.width - active_enclose_contact + \
self.contact_to_gate
# 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
(self.mults - 1) * self.poly_pitch
# Active height is just the transistor width
self.active_height = self.tx_width
@ -155,32 +155,35 @@ class ptx(design.design):
# Poly height must include poly extension over active
self.poly_height = self.tx_width + 2 * self.poly_extend_active
well_name = "{}well".format(self.well_type)
# The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active] * 2)
if well_name in layer:
well_enclose_active = drc(well_name + "_enclose_active")
self.active_offset = vector([well_enclose_active] * 2)
else:
self.active_offset = vector(0, 0)
# Well enclosure of active, ensure minwidth as well
well_name = "{}well".format(self.well_type)
if well_name in layer:
self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active,
self.well_width)
self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active,
self.well_width)
well_width_rule = drc("minwidth_" + well_name)
well_enclose_active = drc(well_name + "_enclose_active")
self.well_width = max(self.active_width + 2 * well_enclose_active,
well_width_rule)
self.well_height = max(self.active_height + 2 * well_enclose_active,
well_width_rule)
# We are going to shift the 0,0, so include that in the width and height
self.height = self.cell_well_height - self.active_offset.y
self.width = self.cell_well_width - self.active_offset.x
self.height = self.well_height - self.active_offset.y
self.width = self.well_width - self.active_offset.x
else:
# If no well, use the boundary of the active and poly
# The well is not included in the height and width
self.height = self.poly_height
self.width = self.active_width
# The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active] * 2)
# 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,
self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width,
0.5 * self.active_height)
# Min area results are just flagged for now.
debug.check(self.active_width * self.active_height >= self.minarea_active,
"Minimum active area violated.")
@ -231,8 +234,8 @@ class ptx(design.design):
# 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
+ self.m1_space + 0.5 * self.m1_width)
pin_offset = vector(0,
0.5 * self.active_contact.second_layer_height + self.m1_space + 0.5 * self.m1_width)
# This is the width of a m1 extend the ends of the pin
end_offset = vector(self.m1_width / 2.0, 0)
@ -328,26 +331,34 @@ class ptx(design.design):
Add an (optional) well and implant for the type of transistor.
"""
well_name = "{}well".format(self.well_type)
if not (well_name in layer or "vtg" in layer):
return
center_pos = self.active_offset + vector(self.width / 2.0,
self.height / 2.0)
well_ll = center_pos - vector(self.well_width / 2.0,
self.well_height / 2.0)
well_ll = well_ll - vector(0,
self.poly_extend_active)
if well_name in layer:
self.add_rect(layer=well_name,
offset=(0,0),
width=self.cell_well_width,
height=self.cell_well_height)
offset=well_ll,
width=self.well_width,
height=self.well_height)
if "vtg" in layer:
self.add_rect(layer="vtg",
offset=(0,0),
width=self.cell_well_width,
height=self.cell_well_height)
offset=well_ll,
width=self.well_width,
height=self.well_height)
def calculate_num_contacts(self):
"""
"""
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.
@ -361,10 +372,10 @@ class ptx(design.design):
for i in range(self.mults):
if i%2:
# 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_spacing, 0))
else:
# 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_spacing, 0))
return [source_positions,drain_positions]
@ -379,7 +390,7 @@ class ptx(design.design):
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("H","V"),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text="S",
@ -393,7 +404,7 @@ class ptx(design.design):
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("H","V"),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text="D",

View File

@ -40,12 +40,10 @@ class pwrite_driver(design.design):
# Creates the netlist and layout
# Since it has variable height, it is not a pgate.
self.create_netlist()
if not OPTS.netlist_only:
if not OPTS.netlist_only:
self.create_layout()
self.DRC_LVS()
def create_netlist(self):
self.add_pins()
self.add_modules()
@ -55,8 +53,6 @@ class pwrite_driver(design.design):
self.place_modules()
self.route_wires()
self.route_supplies()
def add_pins(self):
self.add_pin("din", "INPUT")
@ -66,17 +62,19 @@ class pwrite_driver(design.design):
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
# Tristate inverter
self.tri = factory.create(module_type="ptristate_inv", height="min")
self.add_mod(self.tri)
debug.check(self.tri.width<self.width,"Could not create tristate inverter to match bitcell width")
debug.check(self.tri.width<self.width,
"Could not create tristate inverter to match bitcell width")
#self.tbuf = factory.create(module_type="ptristate_buf", height="min")
#self.tbuf = factory.create(module_type="ptristate_buf",
#height="min")
#self.add_mod(self.tbuf)
#debug.check(self.tbuf.width<self.width,"Could not create tristate buffer to match bitcell width")
#debug.check(self.tbuf.width<self.width,
#"Could not create tristate buffer to match bitcell width")
# Inverter for din and en
self.inv = factory.create(module_type="pinv", under_rail_vias=True)

View File

@ -145,7 +145,10 @@ drc["minlength_channel"] = 0.05
drc["pwell_to_nwell"] = 0.225
# WELL.3 Minimum spacing of nwell/pwell at the same potential
# WELL.4 Minimum width of nwell/pwell
drc.add_layer("well",
drc.add_layer("nwell",
width = 0.2,
spacing = 0.135)
drc.add_layer("pwell",
width = 0.2,
spacing = 0.135)
@ -174,7 +177,10 @@ drc.add_layer("active",
width = 0.09,
spacing = 0.08)
# ACTIVE.3 Minimum enclosure/spacing of nwell/pwell to active
drc.add_enclosure("well",
drc.add_enclosure("nwell",
layer = "active",
enclosure = 0.055)
drc.add_enclosure("pwell",
layer = "active",
enclosure = 0.055)

View File

@ -126,11 +126,14 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map"
drc["minwidth_tx"] = 4*_lambda_
drc["minlength_channel"] = 2*_lambda_
# 1.4 Minimum spacing between wells of different type (if both are drawn)
# 1.4 Minimum spacing between wells of different type (if both are drawn)
drc["pwell_to_nwell"] = 0
# 1.3 Minimum spacing between wells of same type (if both are drawn)
# 1.1 Minimum width
drc.add_layer("well",
# 1.1 Minimum width
drc.add_layer("nwell",
width = 12*_lambda_,
spacing = 6*_lambda_)
drc.add_layer("pwell",
width = 12*_lambda_,
spacing = 6*_lambda_)
@ -157,7 +160,10 @@ drc.add_layer("active",
spacing = 4*_lambda_)
# 2.3 Source/drain active to well edge
drc.add_enclosure("well",
drc.add_enclosure("nwell",
layer = "active",
enclosure = 6*_lambda_)
drc.add_enclosure("pwell",
layer = "active",
enclosure = 6*_lambda_)