OpenRAM/compiler/nand_3.py

446 lines
20 KiB
Python

import contact
import design
import debug
from tech import drc
from ptx import ptx
from vector import vector
from tech import parameter, spice
from globals import OPTS
class nand_3(design.design):
"""
This module generates gds of a parametrically sized 3_input nand.
This model use ptx to generate a 3_input nand within a cetrain height.
The 3_input nand's cell_height should be the same as the 6t library cell.
This module doesn't generate multi_finger 3_input nand gate,
It generate only the minimum size 3_input nand gate
"""
c = reload(__import__(OPTS.config.bitcell))
bitcell = getattr(c, OPTS.config.bitcell)
unique_id = 1
def __init__(self, nmos_width=3*drc["minwidth_tx"], height=bitcell.height):
"""Constructor : Creates a cell for a simple 3 input nand"""
name = "nand3_{0}".format(nand_3.unique_id)
nand_3.unique_id += 1
design.design.__init__(self, name)
debug.info(2, "create nand_3 structure {0} with size of {1}".format(name, nmos_width))
self.nmos_size = nmos_width
# FIXME: Why is this??
self.pmos_size = 2 * nmos_width / 3
self.tx_mults = 1
self.height = height
self.add_pins()
self.create_layout()
self.DRC_LVS()
def add_pins(self):
""" add pics for this module """
self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"])
def create_layout(self):
""" create layout """
self.create_ptx()
self.setup_layout_constants()
self.add_rails()
self.add_ptx()
self.add_well_contacts()
# These aren't for instantiating, but we use them to get the dimensions
self.poly_contact = contact.contact(("poly", "contact", "metal1"))
self.m1m2_via = contact.contact(("metal1", "via1", "metal2"))
self.connect_tx()
self.connect_well_contacts()
self.extend_wells()
self.extend_active()
self.connect_rails()
self.route_pins()
def create_ptx(self):
""" Create ptx but not yet placed"""
self.nmos = ptx(width=self.nmos_size,
mults=self.tx_mults,
tx_type="nmos")
self.add_mod(self.nmos)
self.add_mod(self.nmos)
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_size,
mults=self.tx_mults,
tx_type="pmos")
self.add_mod(self.pmos)
self.add_mod(self.pmos)
self.add_mod(self.pmos)
def setup_layout_constants(self):
""" setup layout constraints """
self.well_width = self.nmos.active_position.x \
+ 3 * self.pmos.active_width + drc["active_to_body_active"] \
+ drc["well_enclosure_active"] - self.nmos.active_contact.width \
+ self.pmos.active_contact.height + drc["minwidth_metal1"] \
+ (drc["metal1_to_metal1"] - drc["well_enclosure_active"])
self.width = self.width = self.well_width
def add_rails(self):
""" Add VDD and GND rails """
rail_width = self.width
self.rail_height = rail_height = drc["minwidth_metal1"]
# Relocate the origin
self.gnd_loc = vector(0 , - 0.5 * drc["minwidth_metal1"])
self.add_layout_pin(text="gnd",
layer="metal1",
offset=self.gnd_loc,
width=rail_width,
height=rail_height)
self.vdd_loc = vector(0, self.height - 0.5 * drc["minwidth_metal1"])
self.add_layout_pin(text="vdd",
layer="metal1",
offset=self.vdd_loc,
width=rail_width,
height=rail_height)
def add_ptx(self):
""" transistors are added and placed inside the layout """
# determines the spacing between the edge and nmos (rail to active
# metal or poly_to_poly spacing)
self.edge_to_nmos = max(drc["metal1_to_metal1"]
- self.nmos.active_contact_positions[0].y,
0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"]
- self.nmos.poly_positions[0].y)
# Extra offset is calculated to make room for B and C contancts
xoffset = (self.nmos.active_contact.width
+ drc["minwidth_metal1"]
+ (drc["metal1_to_metal1"] - drc["well_enclosure_active"]))
# determine the position of the first transistor from the left
self.nmos_position1 = vector(xoffset,
0.5 * drc["minwidth_metal1"] + self.edge_to_nmos)
offset = self.nmos_position1 + vector(0,self.nmos.height)
self.add_inst(name="nmos",
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["net2", "A", "gnd", "gnd"])
self.nmos_position2 = (self.nmos_position1
+ vector(self.nmos.active_width,0)
- vector(self.nmos.active_contact.width,0))
offset = self.nmos_position2 + vector(0, self.nmos.height)
self.add_inst(name="nmos2",
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["net1", "B", "net2", "gnd"])
p2tp3 = vector(self.nmos.active_width - self.nmos.active_contact.width,
self.nmos.height)
self.nmos_position3 = self.nmos_position2 + p2tp3
self.add_inst(name="nmos3",
mod=self.nmos,
offset=self.nmos_position3,
mirror="MX")
self.connect_inst(["Z", "C", "net1", "gnd"])
# determines the spacing between the edge and pmos
self.edge_to_pmos = max(drc["metal1_to_metal1"]
- self.pmos.active_contact_positions[0].y,
0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"]
- self.pmos.poly_positions[0].y)
self.pmos_position1 = vector(self.nmos_position1.x,
self.height - 0.5 * drc["minwidth_metal1"]
- self.pmos.height - self.edge_to_pmos)
self.add_inst(name="pmos1",
mod=self.pmos,
offset=self.pmos_position1)
self.connect_inst(["Z", "A", "vdd", "vdd"])
self.pmos_position2 = vector(self.nmos_position2.x, self.pmos_position1.y)
self.add_inst(name="pmos2",
mod=self.pmos,
offset=self.pmos_position2)
self.connect_inst(["vdd", "B", "Z", "vdd"])
self.pmos_position3 = vector(self.nmos_position3.x, self.pmos_position1.y)
self.add_inst(name="pmos3",
mod=self.pmos,
offset=self.pmos_position3)
self.connect_inst(["Z", "C", "vdd", "vdd"])
def add_well_contacts(self):
""" create well contacts """
layer_stack = ("active", "contact", "metal1")
xoffset = (self.nmos_position3.x + self.pmos.active_position.x
+ self.pmos.active_width + drc["active_to_body_active"])
yoffset = self.pmos_position1.y + self.pmos.active_contact_positions[0].y
self.nwell_contact_position = vector(xoffset, yoffset)
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos.num_of_tacts))
xoffset = self.nmos_position3.x + (self.nmos.active_position.x
+ self.nmos.active_width
+ drc["active_to_body_active"])
yoffset = self.nmos_position1.y + (self.nmos.height
- self.nmos.active_contact_positions[0].y
- self.nmos.active_contact.height)
self.pwell_contact_position = vector(xoffset, yoffset)
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos.num_of_tacts))
def connect_well_contacts(self):
""" Connect well contacts to vdd and gnd rail """
well_tap_length = self.height - self.nwell_contact_position.y
xoffset = (self.nwell_contact_position.x
+ self.nwell_contact.second_layer_position.x
- self.nwell_contact.first_layer_position.x)
offset = [xoffset, self.nwell_contact_position.y]
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=well_tap_length)
well_tap_length = self.nmos.active_height
offset = vector(self.pwell_contact_position
+ self.pwell_contact.second_layer_position
- self.pwell_contact.first_layer_position).scale(1,0)
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=well_tap_length)
def connect_rails(self):
""" Connect transistor pmos drains to vdd and nmos drains to gnd rail """
correct = vector(self.pmos.active_contact.width - drc["minwidth_metal1"],
0).scale(0.5,0)
poffset = self.pmos_position1 + self.pmos.active_contact_positions[0] + correct
temp_height = self.height - poffset.y
self.add_rect(layer="metal1",
offset=poffset,
width=drc["minwidth_metal1"],
height=temp_height)
poffset = [self.pmos_position3.x + self.pmos.active_contact_positions[0].x + correct.x,
poffset.y]
self.add_rect(layer="metal1",
offset=poffset,
width=drc["minwidth_metal1"],
height=temp_height)
poffset = self.nmos_position1 + self.nmos.active_contact_positions[0] + correct
self.add_rect(layer="metal1",
offset=poffset.scale(1,0),
width=drc["minwidth_metal1"],
height=temp_height)
def connect_tx(self):
""" poly and drain connections """
self.connect_poly()
self.connect_drains()
def connect_poly(self):
""" Connect poly """
yoffset_nmos = (self.nmos_position1.y + self.nmos.poly_positions[0].y
+ self.nmos.poly_height)
poly_length = (self.pmos_position1.y + self.pmos.poly_positions[0].y
- yoffset_nmos + drc["minwidth_poly"])
offset = vector(self.nmos_position1.x + self.nmos.poly_positions[0].x,
yoffset_nmos - drc["minwidth_poly"])
self.add_rect(layer="poly",
offset=offset,
width=drc["minwidth_poly"],
height=poly_length)
self.add_rect(layer="poly",
offset=[offset.x + self.pmos.active_contact.width + 2 * drc["minwidth_poly"],
offset.y],
width=drc["minwidth_poly"],
height=poly_length)
self.add_rect(layer="poly",
offset=[offset.x + 2 * self.pmos.active_contact.width + 4 * drc["minwidth_poly"],
offset.y],
width=drc["minwidth_poly"],
height=poly_length)
def connect_drains(self):
""" Connect pmos and nmos drains. The output will be routed to this connection point. """
yoffset = drc["minwidth_metal1"] + (self.nmos.active_contact_positions[0].y
- drc["well_enclosure_active"]
+ drc["metal1_to_metal1"])
drain_length = (self.height - yoffset + 0.5 * drc["minwidth_metal1"]
- (self.pmos.height - self.pmos.active_contact_positions[0].y))
layer_stack = ("metal1", "via1", "metal2")
for position in self.pmos.active_contact_positions[1:][::2]:
diff_active_via = self.pmos.active_contact.width - self.m1m2_via.second_layer_width
offset = (self.pmos_position2 + self.pmos.active_contact_positions[0]
+ vector(diff_active_via / 2,0))
self.add_via(layer_stack,offset)
width = (2 * self.pmos.active_width
- self.pmos.active_contact.width
- (self.pmos.active_contact.width
- self.m1m2_via.second_layer_width))
self.add_rect(layer="metal2",
offset=offset,
width=width,
height=self.m1m2_via.second_layer_width)
xoffset = (self.pmos_position3.x + self.pmos.active_contact_positions[1].x
+ diff_active_via / 2)
self.add_via(layer_stack,[xoffset,offset.y])
xoffset = (self.nmos_position3.x + self.nmos.active_position.x
+ self.nmos.active_width - self.nmos.active_contact.width / 2)
self.drain_position = vector(xoffset,
drc["minwidth_metal1"] + drc["metal1_to_metal1"])
length = self.height - 2 * (drc["minwidth_metal1"] + drc["metal1_to_metal1"])
self.add_rect(layer="metal1",
offset=self.drain_position,
width=drc["minwidth_metal1"],
height=length)
def route_pins(self):
""" input anbd output routing """
self.route_input_gate_A()
self.route_input_gate_B()
self.route_input_gate_C()
self.route_output()
def route_input_gate_A(self):
""" routing for input A """
offset = self.pmos_position1 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height)
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=via_offset)
self.add_layout_pin(text="A",
layer="metal1",
offset=offset.scale(0,1),
width=offset.x,
height=drc["minwidth_metal1"])
def route_input_gate_B(self):
""" routing for input B """
offset = self.pmos_position2 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height+1*(self.m1m2_via.width+drc["metal1_to_metal1"]))
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=via_offset)
self.add_layout_pin(text="B",
layer="metal1",
offset=offset.scale(0,1),
width=offset.x,
height=drc["minwidth_metal1"])
def route_input_gate_C(self):
""" routing for input C """
offset = self.pmos_position3 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height+2*(self.m1m2_via.width+drc["metal1_to_metal1"]))
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=via_offset)
self.add_layout_pin(text="C",
layer="metal1",
offset=offset.scale(0,1),
width=offset.x,
height=drc["minwidth_metal1"])
def route_output(self):
""" routing for output Z """
xoffset = self.nmos_position3.x + self.nmos.active_position.x \
+ self.nmos.active_width - self.nmos.active_contact.width / 2
yoffset = (self.nmos.height + (self.height - drc["minwidth_metal1"]
- self.pmos.height - self.nmos.height) / 2
- (drc["minwidth_metal1"] / 2))
self.add_layout_pin(text="Z",
layer="metal1",
offset=vector(xoffset, yoffset),
width=self.well_width - xoffset,
height=drc["minwidth_metal1"])
def extend_wells(self):
""" extension of well """
middle_point = self.nmos_position1.y + self.nmos.pwell_position.y \
+ self.nmos.well_height + (self.pmos_position1.y
+ self.pmos.nwell_position.y
- self.nmos_position1.y
- self.nmos.pwell_position.y
- self.nmos.well_height) / 2
offset = self.nwell_position = vector(0, middle_point)
self.nwell_height = self.height - middle_point
self.add_rect(layer="nwell",
offset=offset,
width=self.well_width,
height=self.nwell_height)
self.add_rect(layer="vtg",
offset=offset,
width=self.well_width,
height=self.nwell_height)
offset = self.pwell_position = vector(0, 0)
self.pwell_height = middle_point
self.add_rect(layer="pwell",
offset=offset, width=self.well_width,
height=self.pwell_height)
self.add_rect(layer="vtg",
offset=offset,
width=self.well_width,
height=self.pwell_height)
def extend_active(self):
""" extension of active region """
self.active_width = self.pmos.active_width \
+ drc["active_to_body_active"] + self.pmos.active_contact.width
offset = (self.pmos.active_position+self.pmos_position3.scale(1,0)
+ self.pmos_position1.scale(0,1))
self.add_rect(layer="active",
offset=offset,
width=self.active_width,
height=self.pmos.active_height)
offset = offset + vector(self.pmos.active_width,0)
width = self.active_width - self.pmos.active_width
self.add_rect(layer="nimplant",
offset=offset,
width=width,
height=self.pmos.active_height)
offset = [self.nmos_position3.x + self.nmos.active_position.x,
self.nmos_position1.y + self.nmos.height
- self.nmos.active_position.y - self.nmos.active_height]
self.add_rect(layer="active",
offset=offset,
width=self.active_width,
height=self.nmos.active_height)
offset = offset + vector(self.nmos.active_width,0)
width = self.active_width - self.nmos.active_width
self.add_rect(layer="pimplant",
offset=offset,
width=width,
height=self.nmos.active_height)
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def delay(self, slew, load=0.0):
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew)