Fixed SCMOS bugs.

This commit is contained in:
Matt Guthaus 2017-11-30 15:58:16 -08:00
parent c4ce646b81
commit 44faa8d58d
2 changed files with 37 additions and 33 deletions

View File

@ -81,26 +81,33 @@ class pinv(design.design):
Determines the number of fingers needed to achieve the size within
the height constraint. This may fail if the user has a tight height.
"""
self.m1_width = drc["minwidth_metal1"]
# Do a quick sanity check and bail if unlikely feasible height
# Sanity check. can we make an inverter in the height with minimum tx sizes?
# Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain)
# plus the tx height
nmos = ptx(tx_type="nmos")
pmos = ptx(width=self.beta*drc["minwidth_tx"], tx_type="pmos")
tx_height = nmos.height + pmos.height
pmos = ptx(width=drc["minwidth_tx"], tx_type="pmos")
tx_height = nmos.poly_height + pmos.poly_height
# rotated m1 pitch or poly to active spacing
min_channel = max(self.poly_contact.width + drc["metal1_to_metal1"],
self.poly_contact.width + 2*drc["poly_to_active"])
print min_channel
debug.check(self.height>tx_height + min_channel,"Cell height too small for our simple design rules.")
# This is a poly-to-poly of a flipped cell
# plus the extension beyond active in the ptx plus the drain/source connection
self.top_bottom_cell_space = 0.5*drc["poly_to_poly"]+drc["poly_extend_active"] + 0.5*self.m1_width
total_height = tx_height + min_channel + 2*self.top_bottom_cell_space
debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height))
# Determine the height left to the transistors to determine the number of fingers
tx_height_available = self.height - min_channel
tx_height_available = self.height - min_channel - 2*self.top_bottom_cell_space
# Divide the height in half. Could divide proportional to beta, but this makes
# connecting wells of multiple cells easier.
nmos_height_available = 0.5 * tx_height_available
pmos_height_available = 0.5 * tx_height_available
# Subtract the poly space under the rail of the tx
nmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"]
pmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"]
debug.info(2,"Height avail {0} PMOS height {1} NMOS height {2}".format(tx_height_available, nmos_height_available, pmos_height_available))
# Determine the number of mults for each to fit width into available space
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
@ -126,8 +133,6 @@ class pinv(design.design):
Pre-compute some handy layout parameters.
"""
self.m1_width = drc["minwidth_metal1"]
# 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 \
@ -174,19 +179,19 @@ class pinv(design.design):
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
# place PMOS so it is half a poly spacing down from the top
pmos_position = vector(0,self.height-self.pmos.height-0.5*drc["poly_to_poly"])
self.pmos_pos = self.pmos.active_offset.scale(1,0) + vector(0, self.height-self.pmos.active_height-self.top_bottom_cell_space)
self.pmos_inst=self.add_inst(name="pinv_pmos",
mod=self.pmos,
offset=pmos_position)
offset=self.pmos_pos)
self.connect_inst(["Z", "A", "vdd", "vdd"])
# place NMOS so that it is half a poly spacing up from the bottom
nmos_position = vector(0,self.nmos.height+0.5*drc["poly_to_poly"])
self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.nmos.active_height+self.top_bottom_cell_space)
self.nmos_inst=self.add_inst(name="pinv_nmos",
mod=self.nmos,
offset=nmos_position,
offset=self.nmos_pos,
mirror="MX")
self.connect_inst(["Z", "A", "gnd", "gnd"])
@ -293,9 +298,9 @@ class pinv(design.design):
nmos_drain_pin = self.nmos_inst.get_pin("D")
pmos_drain_pin = self.pmos_inst.get_pin("D")
# Pick point in center of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.uc()
pmos_drain_pos = vector(nmos_drain_pin.uc().x,pmos_drain_pin.bc().y)
# Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.ur() - vector(0.5*self.m1_width,0)
pmos_drain_pos = vector(nmos_drain_pos.x,pmos_drain_pin.bc().y)
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos])
# Remember the mid for the output
@ -321,20 +326,18 @@ class pinv(design.design):
layer_stack = ("active", "contact", "metal1")
# Lower left of the NMOS active
nmos_pos = self.nmos_inst.ll() + self.nmos.active_offset
# To the right a spacing away from the nmos right active edge
nwell_contact_offset = self.nmos.active_width + drc["active_to_body_active"]
nwell_offset = nmos_pos + vector(nwell_contact_offset, 0)
nwell_contact_xoffset = self.nmos_pos.x + self.nmos.active_width + drc["active_to_body_active"]
nwell_contact_yoffset = self.nmos_pos.y - self.nmos.active_height
nwell_offset = vector(nwell_contact_xoffset, nwell_contact_yoffset)
# Offset by half a contact in x and y
nwell_offset += vector(0.5*self.nmos.active_contact.first_layer_width,
0.5*self.nmos.active_contact.first_layer_height)
self.nwell_contact=self.add_contact_center(layers=layer_stack,
offset=nwell_offset,
size=(1,self.nmos.num_contacts))
offset=nwell_offset)
self.add_path("metal1",[nwell_offset,nwell_offset.scale(1,0)])
# Now add the full active and implant for the PMOS
nwell_offset = nmos_pos + vector(self.nmos.active_width,0)
nwell_offset = self.nmos_pos + vector(self.nmos.active_width,-self.nmos.active_height)
nwell_contact_width = drc["active_to_body_active"] + self.nmos.active_contact.width
self.add_rect(layer="active",
offset=nwell_offset,
@ -347,20 +350,18 @@ class pinv(design.design):
# Lower left of the PMOS active
pmos_pos = self.pmos_inst.ll() + self.pmos.active_offset
pwell_contact_offset = self.pmos.active_width + drc["active_to_body_active"]
# To the right a spacing away from the pmos right active edge
pwell_offset = pmos_pos + vector(pwell_contact_offset, 0)
pwell_contact_xoffset = self.pmos_pos.x + self.pmos.active_width + drc["active_to_body_active"]
pwell_contact_yoffset = self.pmos_pos.y + self.pmos.active_height - self.pmos.active_contact.height
pwell_offset = vector(pwell_contact_xoffset, pwell_contact_yoffset)
# Offset by half a contact
pwell_offset += vector(0.5*self.pmos.active_contact.first_layer_width,
0.5*self.pmos.active_contact.first_layer_height)
self.pwell_contact=self.add_contact_center(layers=layer_stack,
offset=pwell_offset,
size=(1,self.pmos.num_contacts))
offset=pwell_offset)
self.add_path("metal1",[pwell_offset,vector(pwell_offset.x,self.height)])
# Now add the full active and implant for the PMOS
pwell_offset = pmos_pos + vector(self.pmos.active_width,0)
pwell_offset = self.pmos_pos + vector(self.pmos.active_width,0)
pwell_contact_width = drc["active_to_body_active"] + self.pmos.active_contact.width
self.add_rect(layer="active",
offset=pwell_offset,

View File

@ -43,6 +43,8 @@ class ptx(design.design):
self.create_spice()
self.create_layout()
self.translate_all(self.active_offset)
# for run-time, we won't check every transitor DRC independently
# but this may be uncommented for debug purposes
#self.DRC()
@ -214,7 +216,8 @@ class ptx(design.design):
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_offset = self.active_offset + vector(self.poly_width,self.poly_height).scale(0.5,0.5) \
+ vector(self.end_to_poly, -self.poly_extend_active)
# poly_positions are the bottom center of the poly gates
poly_positions = []