Merge branch 'rewrite_ptx' into dev

This commit is contained in:
Matt Guthaus 2017-12-12 15:05:03 -08:00
commit 18e748f3c0
44 changed files with 14475 additions and 14875 deletions

View File

@ -4,10 +4,10 @@ import debug
import design
import math
from math import log,sqrt,ceil
from contact import contact
import contact
from pinv import pinv
from nand_2 import nand_2
from nor_2 import nor_2
from pnand2 import pnand2
from pnor2 import pnor2
from vector import vector
from globals import OPTS
@ -128,8 +128,8 @@ class bank(design.design):
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.")
# Width for left gnd rail
self.vdd_rail_width = 5*drc["minwidth_metal2"]
self.gnd_rail_width = 5*drc["minwidth_metal2"]
self.vdd_rail_width = 5*self.m2_width
self.gnd_rail_width = 5*self.m2_width
# Number of control lines in the bus
self.num_control_lines = 6
@ -147,13 +147,8 @@ class bank(design.design):
self.num_addr_lines = self.row_addr_size
# M1/M2 routing pitch is based on contacted pitch
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
self.m1_pitch = self.m1m2_via.height + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = self.m2m3_via.height + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.m1_width = drc["minwidth_metal1"]
self.m2_width = drc["minwidth_metal2"]
self.m3_width = drc["minwidth_metal3"]
self.m1_pitch = contact.m1m2.height + max(self.m1_space,self.m2_space)
self.m2_pitch = contact.m2m3.height + max(self.m2_space,self.m3_space)
# Overall central bus gap. It includes all the column mux lines,
# control lines, address flop to decoder lines and a GND power rail in M2
@ -177,8 +172,7 @@ class bank(design.design):
rows=self.num_rows)
self.add_mod(self.bitcell_array)
self.precharge_array = self.mod_precharge_array(columns=self.num_cols,
ptx_width=drc["minwidth_tx"])
self.precharge_array = self.mod_precharge_array(columns=self.num_cols)
self.add_mod(self.precharge_array)
if(self.col_addr_size > 0):
@ -218,14 +212,6 @@ class bank(design.design):
self.inv = pinv()
self.add_mod(self.inv)
# Vertical metal rail gap definition
self.metal2_extend_contact = (self.m1m2_via.second_layer_height
- self.m1m2_via.contact_width) / 2
self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"]
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"]
self.via_shift = (self.m1m2_via.second_layer_width
- self.m1m2_via.first_layer_width) / 2
self.via_shift_offset = vector(0,self.via_shift)
def add_bitcell_array(self):
""" Adding Bitcell Array """
@ -473,14 +459,14 @@ class bank(design.design):
self.inv4x = pinv(4)
self.add_mod(self.inv4x)
self.nor2 = nor_2()
self.nor2 = pnor2()
self.add_mod(self.nor2)
self.nand2 = nand_2()
self.nand2 = pnand2()
self.add_mod(self.nand2)
# left of gnd rail is the "bus start"
bus_start = self.gnd_x_offset - drc["metal2_to_metal2"]
bus_start = self.gnd_x_offset - self.m2_space
xoffset_nand = bus_start - self.nand2.width - self.inv4x.width - drc["pwell_to_nwell"]
xoffset_nor = bus_start - self.nor2.width - self.inv4x.width - drc["pwell_to_nwell"]
xoffset_inv = bus_start - self.inv4x.width
@ -497,42 +483,35 @@ class bank(design.design):
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
# bank_sel is vertical wire
xoffset_bank_sel = xoffset_bank_sel_inv
bank_sel_inv_pin = bank_sel_inv.get_pin("A")
xoffset_bank_sel = bank_sel_inv_pin.lx()
bank_sel_line_pos = vector(xoffset_bank_sel, self.min_point)
self.add_label_pin(text="bank_sel",
layer="metal2",
offset=bank_sel_line_pos,
width=self.m2_width,
height=self.decoder_min_point-self.min_point-self.m2_pitch)
bank_sel_inv_in_pos = bank_sel_inv.get_pin("A").lc()
bank_sel_line_end = vector(xoffset_bank_sel, self.decoder_min_point-self.m2_pitch)
self.add_path("metal2", [bank_sel_line_pos,bank_sel_line_end])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=bank_sel_inv_in_pos,
rotate=90)
offset=bank_sel_inv_pin.lc())
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)
# Route the pin to the left edge as well
bank_sel_pin_pos=vector(self.left_vdd_x_offset, self.min_point)
bank_sel_pin_end=vector(bank_sel_line_pos.x, bank_sel_pin_pos.y)
self.add_layout_pin_center_segment(text="bank_sel",
layer="metal3",
start=bank_sel_pin_pos,
end=bank_sel_line_pos)
end=bank_sel_pin_end)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=bank_sel_line_pos,
offset=bank_sel_pin_end,
rotate=90)
# bank_sel_bar is vertical wire
xoffset_bank_sel_bar = bank_sel_inv.get_pin("Z").rx()
bank_sel_bar_pin = bank_sel_inv.get_pin("Z")
xoffset_bank_sel_bar = bank_sel_bar_pin.rx()
self.add_label_pin(text="bank_sel_bar",
layer="metal2",
offset=vector(xoffset_bank_sel_bar, self.min_point),
width=self.m2_width,
height=2*self.inv.height)
bank_sel_out_pin = bank_sel_inv.get_pin("Z").rc()
self.add_via_center(layers=("metal1","via1","metal2"),
offset=bank_sel_out_pin)
offset=bank_sel_bar_pin.rc())
for i in range(self.num_control_lines):
input_name = self.input_control_signals[i]
gated_name = self.control_signals[i]
@ -596,8 +575,8 @@ class bank(design.design):
self.add_path("metal1", [pre, out_position, in_position, post])
# Connect the inverter output to the central bus
out_pos = inv_inst.get_pin("Z").rc() - vector(0.5*self.m1m2_via.height,0)
bus_pos = vector(self.central_line_xoffset[gated_name] + 0.5*self.m2_width, out_pos.y)
out_pos = inv_inst.get_pin("Z").rc()
bus_pos = vector(self.central_line_xoffset[gated_name], out_pos.y)
self.add_path("metal3",[out_pos, bus_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=bus_pos,
@ -610,11 +589,11 @@ class bank(design.design):
rotate=90)
# Connect the logic B input to bank_sel/bank_sel_bar
logic_pin = logic_inst.get_pin("B")
input_pos = vector(xoffset_bank_signal,logic_pin.cy())
self.add_path("metal2",[logic_pin.lc(), input_pos])
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1m2.height,0)
input_pos = vector(xoffset_bank_signal,logic_pos.y)
self.add_path("metal2",[logic_pos, input_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=logic_pin.lc(),
offset=logic_pos,
rotate=90)
@ -624,11 +603,9 @@ class bank(design.design):
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=logic_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=logic_pos,
rotate=90)
self.add_layout_pin_center_segment(text=input_name,
layer="metal3",
start=input_pos,
@ -637,17 +614,16 @@ class bank(design.design):
# Add vdd/gnd supply rails
gnd_pin = inv_inst.get_pin("gnd")
left_gnd_pos = vector(xoffset_bank_sel_inv,gnd_pin.rc().y)
right_gnd_pos = vector(self.gnd_x_offset,gnd_pin.rc().y)
gnd_pos = inv_inst.get_pin("gnd").cy()
left_gnd_pos = vector(xoffset_bank_sel_inv, gnd_pos)
right_gnd_pos = vector(self.gnd_x_offset + 0.5*contact.m1m2.height, gnd_pos)
self.add_path("metal1",[left_gnd_pos, right_gnd_pos])
right_via_pos = vector(self.gnd_x_offset,gnd_pin.lr().y)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=right_via_pos + vector(self.m1m2_via.height,0) - self.via_shift_offset,
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=right_gnd_pos,
rotate=90)
vdd_pin = inv_inst.get_pin("vdd")
left_vdd_pos = vector(self.left_vdd_x_offset,vdd_pin.lc().y)
left_vdd_pos = vector(self.left_vdd_x_offset,vdd_pin.cy())
self.add_path("metal1",[left_vdd_pos,vdd_pin.rc()])
@ -716,8 +692,8 @@ class bank(design.design):
# Control lines (to the right of the GND rail)
for i in range(self.num_control_lines):
x_offset = self.start_of_right_central_bus + i * self.m2_pitch
self.central_line_xoffset[self.control_signals[i]]=x_offset
x_offset = self.start_of_right_central_bus + i*self.m2_pitch
self.central_line_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width
# Pins are added later if this is a single bank, so just add rectangle now
self.add_rect(layer="metal2",
offset=vector(x_offset, self.min_point),
@ -727,9 +703,9 @@ class bank(design.design):
# row address lines (to the left of the column mux or GND rail)
# goes from 0 down to the bottom of the address flops
for i in range(self.row_addr_size):
x_offset = self.start_of_left_central_bus + i * self.m2_pitch
x_offset = self.start_of_left_central_bus + i*self.m2_pitch
name = "A[{}]".format(i)
self.central_line_xoffset[name]=x_offset
self.central_line_xoffset[name]=x_offset + 0.5*self.m2_width
# Add a label pin for LVS correspondence and visual help inspecting the rail.
self.add_label_pin(text=name,
layer="metal2",
@ -741,9 +717,9 @@ class bank(design.design):
# goes from 0 down to the min point
if self.col_addr_size>0:
for i in range(2**self.col_addr_size):
x_offset = self.start_of_left_central_bus + (i + self.row_addr_size) * self.m2_pitch
x_offset = self.start_of_left_central_bus + (i+self.row_addr_size)*self.m2_pitch
name = "sel[{}]".format(i)
self.central_line_xoffset[name]=x_offset
self.central_line_xoffset[name]=x_offset + 0.5*self.m2_width
# Add a label pin for LVS correspondence
self.add_label_pin(text=name,
layer="metal2",
@ -751,10 +727,6 @@ class bank(design.design):
width=self.m2_width,
height=-self.decoder_min_point)
def route_precharge_to_bitcell_array(self):
""" Routing of BL and BR between pre-charge and bitcell array """
@ -836,17 +808,16 @@ class bank(design.design):
# Connect the address rails to the decoder
# Note that the decoder inputs are long vertical rails so spread out the connections vertically.
decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() + vector(0,position_heights*self.bitcell.height+self.m2_pitch)
rail_position = vector(self.central_line_xoffset["A[{}]".format(i)]+drc["minwidth_metal2"],decoder_in_position.y)
rail_position = vector(self.central_line_xoffset["A[{}]".format(i)],decoder_in_position.y)
self.add_path("metal1",[decoder_in_position,rail_position])
decoder_in_via = decoder_in_position - vector(0,0.5*drc["minwidth_metal2"])
decoder_in_via = decoder_in_position - vector(0,0.5*self.m2_width)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=decoder_in_via,
rotate=90)
contact_offset = rail_position - vector(0,0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=contact_offset,
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_position,
rotate=90)
# Route the power and ground, but only BELOW the y=0 since the
@ -856,7 +827,7 @@ class bank(design.design):
if gnd_pin.uy()>0:
continue
decoder_gnd_position = gnd_pin.rc()
via_position = decoder_gnd_position + vector(0.5*self.m1m2_via.height+drc["metal2_to_metal2"],0)
via_position = decoder_gnd_position + vector(0.5*contact.m1m2.height+self.m2_space,0)
gnd_rail_position = vector(self.gnd_x_offset, decoder_gnd_position.y)
self.add_path("metal1", [decoder_gnd_position, via_position])
self.add_path("metal3", [via_position, gnd_rail_position])
@ -886,34 +857,36 @@ class bank(design.design):
# we don't care about bends after connecting to the input pin, so let the path code decide.
for i in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
pre = self.row_decoder_inst.get_pin("decode[{}]".format(i)).lc()
decoder_out_position = self.row_decoder_inst.get_pin("decode[{}]".format(i)).rc() + vector(0.5*self.m1_width,0)
driver_in_position = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc() + vector(0.5*self.m1_width,0)
post = self.wordline_driver_inst.get_pin("in[{}]".format(i)).rc()
self.add_path("metal1", [pre, decoder_out_position, driver_in_position, post])
decoder_out_pos = self.row_decoder_inst.get_pin("decode[{}]".format(i)).rc()
driver_in_pos = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc()
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0)
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1)
self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos])
# The mid guarantees we exit the input cell to the right.
driver_wl_position = self.wordline_driver_inst.get_pin("wl[{}]".format(i)).rc()
mid = driver_wl_position + vector(self.m1_pitch,0)
bitcell_wl_position = self.bitcell_array_inst.get_pin("wl[{}]".format(i)).lc()
self.add_path("metal1", [driver_wl_position, mid, bitcell_wl_position])
driver_wl_pos = self.wordline_driver_inst.get_pin("wl[{}]".format(i)).rc()
bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl[{}]".format(i)).lc()
mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0)
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
# route the gnd rails, add contact to rail as well
for gnd_pin in self.wordline_driver_inst.get_pins("gnd"):
driver_gnd_position = gnd_pin.rc()
right_rail_position = vector(self.bitcell_array_inst.ll().x, driver_gnd_position.y)
self.add_path("metal1", [driver_gnd_position, right_rail_position])
gnd_rail_position = vector(self.gnd_x_offset, driver_gnd_position.y + 0.5*self.m1m2_via.width)
driver_gnd_pos = gnd_pin.rc()
right_rail_pos = vector(self.bitcell_array_inst.ll().x, driver_gnd_pos.y)
self.add_path("metal1", [driver_gnd_pos, right_rail_pos])
gnd_rail_pos = vector(self.gnd_x_offset, driver_gnd_pos.y + 0.5*contact.m1m2.width)
self.add_via(layers=("metal1","via1","metal2"),
offset=gnd_rail_position,
offset=gnd_rail_pos,
rotate=270)
# route the vdd rails
for vdd_pin in self.wordline_driver_inst.get_pins("vdd"):
y_offset = vdd_pin.rc().y
left_rail_position = vector(self.left_vdd_x_offset, y_offset)
right_rail_position = vector(self.right_vdd_x_offset+self.vdd_rail_width, y_offset)
self.add_path("metal1", [left_rail_position, right_rail_position])
left_rail_pos = vector(self.left_vdd_x_offset, y_offset)
right_rail_pos = vector(self.right_vdd_x_offset+self.vdd_rail_width, y_offset)
self.add_path("metal1", [left_rail_pos, right_rail_pos])
@ -926,11 +899,11 @@ class bank(design.design):
# These must be in metal3 so that they don't overlap any gnd lines from decoders
for i in range(2**self.col_addr_size):
name = "sel[{}]".format(i)
mux_addr_position = self.col_mux_array_inst.get_pin(name).lc()
wire_position = vector(self.central_line_xoffset[name]+0.5*self.m2_width, mux_addr_position.y)
self.add_path("metal1", [wire_position,mux_addr_position])
mux_addr_pos = self.col_mux_array_inst.get_pin(name).lc()
wire_pos = vector(self.central_line_xoffset[name], mux_addr_pos.y)
self.add_path("metal1", [wire_pos,mux_addr_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=wire_position,
offset=wire_pos,
rotate=90)
# Take care of the column address decoder routing
@ -943,47 +916,44 @@ class bank(design.design):
for i in range(2**self.col_addr_size):
name = "sel[{}]".format(i)
x_offset = self.central_line_xoffset[name]
decode_out_position = self.col_decoder_inst.get_pin("out[{}]".format(i)).rc()
selx_position = vector(self.central_line_xoffset[name]+drc["minwidth_metal2"],decode_out_position.y)
self.add_path("metal1",[decode_out_position, selx_position])
# via on end
decode_out_via = self.col_decoder_inst.get_pin("out[{}]".format(i)).lr()
selx_via = vector(self.central_line_xoffset[name],decode_out_via.y - drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=selx_via)
decode_out_pos = self.col_decoder_inst.get_pin("out[{}]".format(i)).rc()
selx_pos = vector(self.central_line_xoffset[name],decode_out_pos.y)
self.add_path("metal1",[decode_out_pos, selx_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=selx_pos,
rotate=90)
# route the gnd rails, add contact to rail as well
for gnd_pin in self.col_decoder_inst.get_pins("gnd"):
driver_gnd_position = gnd_pin.rc()
right_rail_position = vector(self.gnd_x_offset, driver_gnd_position.y)
self.add_path("metal1", [driver_gnd_position, right_rail_position])
gnd_rail_position = vector(self.gnd_x_offset, driver_gnd_position.y + 0.5*self.m1m2_via.width)
driver_gnd_pos = gnd_pin.rc()
right_rail_pos = vector(self.gnd_x_offset, driver_gnd_pos.y)
self.add_path("metal1", [driver_gnd_pos, right_rail_pos])
gnd_rail_pos = vector(self.gnd_x_offset, driver_gnd_pos.y + 0.5*contact.m1m2.width)
self.add_via(layers=("metal1","via1","metal2"),
offset=gnd_rail_position,
offset=gnd_rail_pos,
rotate=270)
# route the vdd rails
for vdd_pin in self.col_decoder_inst.get_pins("vdd"):
y_offset = vdd_pin.rc().y
left_rail_position = vector(self.left_vdd_x_offset, y_offset)
right_rail_position = vector(self.gnd_x_offset, y_offset)
self.add_path("metal1", [left_rail_position, right_rail_position])
left_rail_pos = vector(self.left_vdd_x_offset, y_offset)
right_rail_pos = vector(self.gnd_x_offset, y_offset)
self.add_path("metal1", [left_rail_pos, right_rail_pos])
# The connection between last address flops to the input
# of the column_mux line decoder
for i in range(self.col_addr_size):
ff_index = i + self.row_addr_size
dout_position = self.msf_address_inst.get_pin("dout[{}]".format(ff_index)).rc()
in_position = self.col_decoder_inst.get_pin("in[{}]".format(i)).uc()
mid_position = vector(in_position.x,dout_position.y)
self.add_path("metal3",[dout_position, mid_position, in_position])
dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(ff_index)).rc()
in_pos = self.col_decoder_inst.get_pin("in[{}]".format(i)).uc()
mid_pos = vector(in_pos.x,dout_pos.y)
self.add_path("metal3",[dout_pos, mid_pos, in_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=dout_position,
offset=dout_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=in_position)
offset=in_pos)
@ -994,32 +964,28 @@ class bank(design.design):
# to only select line and dout of that FF to the other select line
elif self.col_addr_size == 1:
dout_bar_position = self.msf_address_inst.get_pin("dout_bar[{}]".format(self.row_addr_size)).rc()
sel0_position = vector(self.central_line_xoffset["sel[0]"]+drc["minwidth_metal2"],dout_bar_position.y)
self.add_path("metal1",[dout_bar_position, sel0_position])
dout_bar_pos = self.msf_address_inst.get_pin("dout_bar[{}]".format(self.row_addr_size)).rc()
sel0_pos = vector(self.central_line_xoffset["sel[0]"],dout_bar_pos.y)
self.add_path("metal1",[dout_bar_pos, sel0_pos])
# two vias on both ends
dout_bar_via = dout_bar_position + vector(self.m1m2_via.height,-0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=dout_bar_via,
rotate=90)
sel0_via = sel0_position - vector(0,0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=sel0_via,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=dout_bar_pos,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=sel0_pos,
rotate=90)
dout_position = self.msf_address_inst.get_pin("dout[{}]".format(self.row_addr_size)).rc()
sel1_position = vector(self.central_line_xoffset["sel[1]"]+drc["minwidth_metal2"],dout_position.y)
self.add_path("metal1",[dout_position, sel1_position])
dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(self.row_addr_size)).rc()
sel1_pos = vector(self.central_line_xoffset["sel[1]"],dout_pos.y)
self.add_path("metal1",[dout_pos, sel1_pos])
# two vias on both ends
dout_via = dout_position + vector(self.m1m2_via.height,-0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=dout_via,
rotate=90)
sel1_via = sel1_position - vector(0,0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=sel1_via,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=dout_pos,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=sel1_pos,
rotate=90)
def route_msf_address(self):
@ -1030,42 +996,40 @@ class bank(design.design):
msf_din_pins = self.msf_address_inst.get_pins("din[{}]".format(i))
for pin in msf_din_pins:
if pin.layer=="metal3":
msf_din_position=pin.ll()
msf_din_pos=pin.ll()
break
address_position = vector(self.left_vdd_x_offset, msf_din_position.y)
address_pos = vector(self.left_vdd_x_offset, msf_din_pos.y)
self.add_layout_pin(text="ADDR[{}]".format(i),
layer="metal3",
offset=address_position,
width=msf_din_position.x - self.left_vdd_x_offset)
offset=address_pos,
width=msf_din_pos.x - self.left_vdd_x_offset)
for i in range(self.row_addr_size):
# Connect the ff outputs to the rails
dout_position = self.msf_address_inst.get_pin("dout[{}]".format(i)).rc()
rail_position = vector(self.central_line_xoffset["A[{}]".format(i)]+drc["minwidth_metal2"],dout_position.y)
self.add_path("metal1",[dout_position, rail_position])
dout_via = self.msf_address_inst.get_pin("dout[{}]".format(i)).lr() + vector(self.m1m2_via.height,0)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=dout_via,
rotate=90)
contact_offset = rail_position - vector(0,0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=contact_offset,
rotate=90)
dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(i)).rc()
rail_pos = vector(self.central_line_xoffset["A[{}]".format(i)],dout_pos.y)
self.add_path("metal1",[dout_pos, rail_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=dout_pos,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos,
rotate=90)
# Connect address FF gnd
for gnd_pin in self.msf_address_inst.get_pins("gnd"):
if gnd_pin.layer != "metal2":
continue
gnd_via = gnd_pin.lr() + vector(self.m1m2_via.height,0)
gnd_via = gnd_pin.lr() + vector(contact.m1m2.height,0)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=gnd_via,
rotate=90)
gnd_offset = gnd_pin.rc()
rail_offset = vector(self.gnd_x_offset+self.m1m2_via.height,gnd_offset.y)
rail_offset = vector(self.gnd_x_offset+contact.m1m2.height,gnd_offset.y)
self.add_path("metal1",[gnd_offset,rail_offset])
rail_via = rail_offset - vector(0,0.5*drc["minwidth_metal2"])
rail_via = rail_offset - vector(0,0.5*self.m2_width)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=rail_via,
rotate=90)
@ -1137,35 +1101,34 @@ class bank(design.design):
connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
for (control_signal, pin_position) in connection:
control_position = vector(self.central_line_xoffset[control_signal] + 0.5*self.m2_width, pin_position.y)
self.add_path("metal1", [control_position, pin_position])
#via_offset = vector(control_x_offset, pin_position.y - 0.5*drc["minwidth_metal2"])
for (control_signal, pin_pos) in connection:
control_pos = vector(self.central_line_xoffset[control_signal], pin_pos.y)
self.add_path("metal1", [control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_position,
offset=control_pos,
rotate=90)
# clk to msf address
control_signal = self.prefix+"clk_buf"
pin_position = self.msf_address_inst.get_pin("clk").uc()
mid_position = pin_position + vector(0,self.m1_pitch)
control_x_offset = self.central_line_xoffset[control_signal]
control_position = vector(control_x_offset + self.m1_width, mid_position.y)
self.add_path("metal1",[pin_position, mid_position, control_position])
control_via_position = vector(control_x_offset, mid_position.y-0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=control_via_position)
pin_pos = self.msf_address_inst.get_pin("clk").uc()
mid_pos = pin_pos + vector(0,self.m1_pitch)
control_pos = vector(self.central_line_xoffset[control_signal], mid_pos.y)
self.add_path("metal1",[pin_pos, mid_pos, control_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos,
rotate=90)
# clk to wordline_driver
control_signal = self.prefix+"clk_buf"
pin_position = self.wordline_driver_inst.get_pin("en").uc()
mid_position = pin_position + vector(0,self.m1_pitch)
pin_pos = self.wordline_driver_inst.get_pin("en").uc()
mid_pos = pin_pos + vector(0,self.m1_pitch)
control_x_offset = self.central_line_xoffset[control_signal]
control_position = vector(control_x_offset + self.m1_width, mid_position.y)
self.add_wire(("metal1","via1","metal2"),[pin_position, mid_position, control_position])
control_via_position = vector(control_x_offset, mid_position.y-0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=control_via_position)
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
control_via_pos = vector(control_x_offset, mid_pos.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_via_pos,
rotate=90)
def route_vdd_supply(self):
@ -1189,10 +1152,10 @@ class bank(design.design):
if gnd_pin.layer != "metal1":
continue
# route to the right hand side of the big rail to reduce via overlaps
pin_position = gnd_pin.lc()
gnd_offset = vector(self.gnd_x_offset+self.gnd_rail_width, pin_position.y)
self.add_path("metal1", [gnd_offset, pin_position])
contact_offset = gnd_offset - vector(0,0.5*drc["minwidth_metal2"])
pin_pos = gnd_pin.lc()
gnd_offset = vector(self.gnd_x_offset+self.gnd_rail_width, pin_pos.y)
self.add_path("metal1", [gnd_offset, pin_pos])
contact_offset = gnd_offset - vector(0,0.5*self.m2_width)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=contact_offset,
rotate=90)
@ -1206,13 +1169,13 @@ class bank(design.design):
# it's not an input pin if we have multiple banks
self.add_label_pin(text=ctrl,
layer="metal2",
offset=vector(x_offset, self.min_point),
offset=vector(x_offset - 0.5*self.m2_width, self.min_point),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin(text=ctrl,
layer="metal2",
offset=vector(x_offset, self.min_point),
offset=vector(x_offset - 0.5*self.m2_width, self.min_point),
width=self.m2_width,
height=self.height)
@ -1220,11 +1183,11 @@ class bank(design.design):
def connect_rail_from_right(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).lc()
rail_position = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_position, rail_position - vector(0,self.m2_pitch)])
rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
# Bring it up to M2 for M2/M3 routing
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin + self.m1m2_via_offset,
offset=in_pin + contact.m1m2.offset,
rotate=90)
self.add_via(layers=("metal2","via2","metal3"),
offset=in_pin + self.m2m3_via_offset,
@ -1234,10 +1197,10 @@ class bank(design.design):
def connect_rail_from_left(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).rc()
rail_position = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_position, rail_position - vector(0,self.m2_pitch)])
rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin + self.m1m2_via_offset,
offset=in_pin + contact.m1m2.offset,
rotate=90)
self.add_via(layers=("metal2","via2","metal3"),
offset=in_pin + self.m2m3_via_offset,

View File

@ -3,6 +3,7 @@ import debug
from tech import drc
from vector import vector
class contact(design.design):
"""
Object for a contact shape with its conductor enclosures.
@ -102,3 +103,14 @@ class contact(design.design):
offset=self.second_layer_position,
width=width,
height=height)
# This is not instantiated and used for calculations only.
# These are static 1x1 contacts to reuse in all the design modules.
well = contact(layer_stack=("active", "contact", "metal1"))
active = contact(layer_stack=("active", "contact", "poly"))
poly = contact(layer_stack=("poly", "contact", "metal1"))
m1m2 = contact(layer_stack=("metal1", "via1", "metal2"))
m2m3 = contact(layer_stack=("metal2", "via2", "metal3"))

View File

@ -2,11 +2,11 @@ from math import log
import design
from tech import drc, parameter
import debug
from contact import contact
import contact
from pinv import pinv
from nand_2 import nand_2
from nand_3 import nand_3
from nor_2 import nor_2
from pnand2 import pnand2
from pnand3 import pnand3
from pnor2 import pnor2
import math
from vector import vector
from globals import OPTS
@ -40,15 +40,15 @@ class control_logic(design.design):
for pin in input_lst + output_lst + rails:
self.add_pin(pin)
self.nand2 = nand_2()
self.nand2 = pnand2()
self.add_mod(self.nand2)
self.nand3 = nand_3()
self.nand3 = pnand3()
self.add_mod(self.nand3)
self.nor2 = nor_2()
self.nor2 = pnor2()
self.add_mod(self.nor2)
# Special gates: inverters for buffering
self.inv = self.inv1 = pinv()
self.inv = self.inv1 = pinv(1)
self.add_mod(self.inv1)
self.inv2 = pinv(2)
self.add_mod(self.inv2)
@ -75,25 +75,21 @@ class control_logic(design.design):
def setup_layout_offsets(self):
""" Setup layout offsets, determine the size of the busses etc """
# These aren't for instantiating, but we use them to get the dimensions
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
self.poly_contact_offset = vector(0.5*self.poly_contact.width,0.5*self.poly_contact.height)
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
# For different layer width vias
self.m1m2_offset_fix = vector(0,0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]))
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
# Have the cell gap leave enough room to route an M2 wire.
# Some cells may have pwell/nwell spacing problems too when the wells are different heights.
self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"])
# Amount to shift a 90 degree rotated via from center-line path routing to it's offset
self.m1m2_via_offset = vector(self.m1m2_via.first_layer_height,-0.5*drc["minwidth_metal2"])
self.m2m3_via_offset = vector(self.m2m3_via.first_layer_height,-0.5*drc["minwidth_metal3"])
self.m1m2_via_offset = vector(contact.m1m2.first_layer_height,-0.5*drc["minwidth_metal2"])
self.m2m3_via_offset = vector(contact.m2m3.first_layer_height,-0.5*drc["minwidth_metal3"])
# First RAIL Parameters: gnd, oe, oebar, cs, we, clk_buf, clk_bar
self.rail_1_start_x = 0
@ -102,13 +98,6 @@ class control_logic(design.design):
self.overall_rail_1_gap = (self.num_rails_1 + 2) * self.m2_pitch
self.rail_1_x_offsets = {}
# Second RAIL Parameters: vdd
self.rail_2_start_x = 0
self.num_rails_2 = 0
self.rail_2_names = ["vdd"]
self.overall_rail_2_gap = (self.num_rails_2 + 2) * self.m2_pitch
self.rail_2_x_offsets = {}
# GAP between main control and replica bitline
self.replica_bitline_gap = 2*self.m2_pitch
@ -121,7 +110,6 @@ class control_logic(design.design):
self.add_1st_row(0)
self.add_2nd_row(self.inv1.height)
self.add_3rd_row(2*self.inv1.height)
self.add_control_routing()
self.add_rbl(0)
self.add_layout_pins()
@ -162,8 +150,11 @@ class control_logic(design.design):
def add_rbl(self,y_off):
""" Add the replica bitline """
# Add to the right of the control rows and routing channel
rows_end_x = max (self.row_1_end_x, self.row_2_end_x, self.row_3_end_x)
self.replica_bitline_offset = vector(self.rail_2_end_x , y_off)
self.replica_bitline_offset = vector(rows_end_x , y_off)
self.rbl=self.add_inst(name="replica_bitline",
mod=self.replica_bitline,
offset=self.replica_bitline_offset,
@ -190,7 +181,9 @@ class control_logic(design.design):
pin=self.clk_inv1.get_pin("A")
self.add_layout_pin(text="clk",
layer="metal1",
offset=pin.ll())
offset=pin.ll(),
width=pin.width(),
height=pin.height())
pin=self.clk_inv1.get_pin("gnd")
self.add_layout_pin(text="gnd",
@ -227,9 +220,18 @@ class control_logic(design.design):
mod=self.inv16,
offset=self.clk_buf_offset)
self.connect_inst(["clk_bar", "clk_buf", "vdd", "gnd"])
# Connect between the inverters
self.add_path("metal1", [self.clk_inv1.get_pin("Z").center(),
self.clk_inv2.get_pin("A").center()])
self.add_path("metal1", [self.clk_inv2.get_pin("Z").center(),
self.clk_bar.get_pin("A").center()])
self.add_path("metal1", [self.clk_bar.get_pin("Z").center(),
self.clk_buf.get_pin("A").center()])
# This is the first rail offset
self.rail_1_start_x = max(self.msf_offset.x + self.msf_control.height,self.clk_buf_offset.x+self.inv16.width)
self.rail_1_start_x = max(self.msf_offset.x + self.msf_control.height,self.clk_buf_offset.x+self.inv16.width) + self.m2_pitch
def add_1st_row(self,y_off):
@ -251,7 +253,7 @@ class control_logic(design.design):
self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"])
#x_off += self.inv1.width
self.row_1_width = x_off
self.row_1_end_x = x_off
def add_2nd_row(self, y_off):
# start after first rails
@ -298,7 +300,7 @@ class control_logic(design.design):
#x_off += self.inv1.width
self.row_2_width = x_off
self.row_2_end_x = x_off
def add_3rd_row(self, y_off):
# start after first rails
@ -312,7 +314,7 @@ class control_logic(design.design):
self.w_en_bar=self.add_inst(name="nand3_w_en_bar",
mod=self.nand3,
offset=self.w_en_bar_offset)
self.connect_inst(["clk_bar", "we", "cs", "w_en_bar", "vdd", "gnd"])
self.connect_inst(["clk_bar", "cs", "we", "w_en_bar", "vdd", "gnd"])
x_off += self.nand3.width
# input: w_en_bar, output: pre_w_en
@ -338,7 +340,7 @@ class control_logic(design.design):
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
#x_off += self.inv1.width
self.row_3_width = x_off
self.row_3_end_x = x_off
def add_control_routing(self):
""" Route the vertical rails for internal control signals """
@ -375,9 +377,12 @@ class control_logic(design.design):
continue
gnd_pin = p.rc()
gnd_rail_position = vector(self.rail_1_x_offsets["gnd"], gnd_pin.y)
self.add_wire(("metal3","via2","metal2"),[gnd_pin, gnd_rail_position, gnd_rail_position - vector(0,self.m2_pitch)])
self.add_via(layers=("metal2","via2","metal3"),
offset=gnd_pin + self.m2m3_via_offset,
self.add_wire(("metal3","via2","metal2"),[gnd_pin, gnd_rail_position])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=gnd_pin,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=gnd_rail_position,
rotate=90)
vdd_pins = self.msf_inst.get_pins("vdd")
@ -386,17 +391,10 @@ class control_logic(design.design):
continue
clk_vdd_position = vector(p.bc().x,self.clk_buf.get_pin("vdd").uy())
self.add_path("metal1",[p.bc(),clk_vdd_position])
self.rail_2_start_x = max (self.row_1_width, self.row_2_width, self.row_3_width)
for i in range(self.num_rails_2):
offset = vector(self.rail_2_start_x + i * self.m1_pitch, 0)
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=self.logic_height)
self.rail_2_x_offsets[self.rail_2_names[i]]=offset.x + 0.5*drc["minwidth_metal1"] # center offset
self.rail_2_end_x = self.rail_2_start_x + (self.num_rails_2+1) * self.m2_pitch
def add_rblk_routing(self):
""" Connect the logic for the rblk generation """
@ -406,72 +404,82 @@ class control_logic(design.design):
# Connect the NAND3 output to the inverter
# The pins are assumed to extend all the way to the cell edge
rblk_bar_pin = self.rblk_bar.get_pin("Z").ur()
inv_in_pin = self.rblk.get_pin("A").ll()
rblk_bar_pin = self.rblk_bar.get_pin("Z").center()
inv_in_pin = self.rblk.get_pin("A").center()
mid1 = vector(inv_in_pin.x,rblk_bar_pin.y)
self.add_path("metal1",[rblk_bar_pin,mid1,inv_in_pin])
# Connect the output to the RBL
rblk_pin = self.rblk.get_pin("Z").rc()
rbl_in_pin = self.rbl.get_pin("en").lc()
rblk_pin = self.rblk.get_pin("Z").center()
rbl_in_pin = self.rbl.get_pin("en").center()
mid1 = vector(rblk_pin.x,rbl_in_pin.y)
self.add_path("metal1",[rblk_pin,mid1,rbl_in_pin])
def connect_rail_from_right(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).lc()
rail_position = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal1","via1","metal2"),[in_pin, rail_position, rail_position - vector(0,self.m2_pitch)])
in_pos = inst.get_pin(pin).center()
rail_pos = vector(self.rail_1_x_offsets[rail], in_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos,
rotate=90)
def connect_rail_from_right_m2m3(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).lc()
rail_position = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_position, rail_position - vector(0,self.m2_pitch)])
in_pos = inst.get_pin(pin).center() - vector(contact.m1m2.height,0)
rail_pos = vector(self.rail_1_x_offsets[rail], in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
# Bring it up to M2 for M2/M3 routing
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin + self.m1m2_via_offset,
self.add_via_center(layers=("metal1","via1","metal2"),
offset=in_pos,
rotate=90)
self.add_via(layers=("metal2","via2","metal3"),
offset=in_pin + self.m2m3_via_offset,
self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rail_pos,
rotate=90)
def connect_rail_from_left(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).rc()
rail_position = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal1","via1","metal2"),[in_pin, rail_position, rail_position - vector(0,self.m2_pitch)])
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin + self.m1m2_via_offset,
in_pos = inst.get_pin(pin).rc()
rail_pos = vector(self.rail_1_x_offsets[rail], in_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=in_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rail_pos,
rotate=90)
def connect_rail_from_left_m2m3(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).rc()
rail_position = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_position, rail_position - vector(0,self.m2_pitch)])
# This via is needed for clk_bar, but is extraneous for the output signals
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin + self.m1m2_via_offset,
in_pos = inst.get_pin(pin).rc()
rail_pos = vector(self.rail_1_x_offsets[rail], in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos,
rotate=90)
self.add_via(layers=("metal2","via2","metal3"),
offset=in_pin + self.m2m3_via_offset,
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rail_pos,
rotate=90)
def add_wen_routing(self):
self.connect_rail_from_right(self.w_en_bar,"A","clk_bar")
self.connect_rail_from_right(self.w_en_bar,"B","we")
self.connect_rail_from_right(self.w_en_bar,"C","cs")
self.connect_rail_from_right(self.w_en_bar,"B","cs")
self.connect_rail_from_right(self.w_en_bar,"C","we")
# Connect the NAND3 output to the inverter
# The pins are assumed to extend all the way to the cell edge
w_en_bar_pin = self.w_en_bar.get_pin("Z").ur()
inv_in_pin = self.pre_w_en.get_pin("A").ll()
w_en_bar_pin = self.w_en_bar.get_pin("Z").center()
inv_in_pin = self.pre_w_en.get_pin("A").center()
mid1 = vector(inv_in_pin.x,w_en_bar_pin.y)
self.add_path("metal1",[w_en_bar_pin,mid1,inv_in_pin])
self.add_path("metal1",[self.pre_w_en.get_pin("Z").center(), self.pre_w_en_bar.get_pin("A").center()])
self.add_path("metal1",[self.pre_w_en_bar.get_pin("Z").center(), self.w_en.get_pin("A").center()])
def add_trien_routing(self):
@ -485,52 +493,64 @@ class control_logic(design.design):
def add_sen_routing(self):
rbl_out_pin = self.rbl.get_pin("out").ul()
in_pin = self.pre_s_en_bar.get_pin("A").rc()
mid1 = vector(rbl_out_pin.x,in_pin.y)
self.add_path("metal1",[rbl_out_pin,mid1,in_pin])
#s_en_pin = self.s_en.get_pin("Z").lc()
rbl_out_pos = self.rbl.get_pin("out").ul()
in_pos = self.pre_s_en_bar.get_pin("A").rc()
mid1 = vector(rbl_out_pos.x,in_pos.y)
self.add_path("metal1",[rbl_out_pos,mid1,in_pos])
#s_en_pos = self.s_en.get_pin("Z").lc()
self.add_path("metal1",[self.pre_s_en_bar.get_pin("Z").center(), self.s_en.get_pin("A").center()])
def add_clk_routing(self):
""" Route the clk and clk_bar signal internally """
# clk_buf
clk_buf_pin = self.clk_buf.get_pin("Z").rc()
clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], clk_buf_pin.y)
self.add_wire(("metal1","via1","metal2"),[clk_buf_pin, clk_buf_rail_position, clk_buf_rail_position - vector(0,self.m2_pitch)])
clk_buf_pos = self.clk_buf.get_pin("Z").rc()
clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], clk_buf_pos.y)
self.add_wire(("metal1","via1","metal2"),[clk_buf_pos, clk_buf_rail_position])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=clk_buf_rail_position,
rotate=90)
# clk_bar
self.connect_rail_from_left_m2m3(self.clk_bar,"Z","clk_bar")
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.clk_bar.get_pin("Z").rc(),
rotate=90)
# clk_buf to msf control flops
msf_clk_pin = self.msf_inst.get_pin("clk").bc()
mid1 = msf_clk_pin - vector(0,self.m2_pitch)
msf_clk_pos = self.msf_inst.get_pin("clk").bc()
mid1 = msf_clk_pos - vector(0,self.m2_pitch)
clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], mid1.y)
# route on M2 to allow vdd connection
self.add_wire(("metal2","via1","metal1"),[msf_clk_pin, mid1, clk_buf_rail_position])
self.add_wire(("metal2","via1","metal1"),[msf_clk_pos, mid1, clk_buf_rail_position])
def connect_right_pin_to_output_pin(self, inst, pin_name, out_name):
""" Create an output pin on the bottom side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name).ur()
self.add_via(layers=("metal1","via1","metal2"),
offset=out_pin + vector(self.m1m2_via.height,-self.m1m2_via.first_layer_width) - self.m1m2_offset_fix,
rotate=90)
self.add_layout_pin(text=out_name,
layer="metal2",
offset=out_pin.scale(1,0),
height=out_pin.y)
out_pin = inst.get_pin(pin_name)
# shift it to the right side of the cell
right_pos=out_pin.center() + vector(inst.rx()-out_pin.cx(),0)
self.add_path("metal1",[out_pin.center(), right_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=right_pos)
self.add_layout_pin_center_segment(text=out_name,
layer="metal2",
start=right_pos.scale(1,0),
end=right_pos)
def connect_left_pin_to_output_pin(self, inst, pin_name, out_name):
""" Create an output pin on the bottom side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name).ul()
self.add_via(layers=("metal1","via1","metal2"),
offset=out_pin + vector(self.m1m2_via.height,-self.m1m2_via.first_layer_width) - self.m1m2_offset_fix,
rotate=90)
self.add_layout_pin(text=out_name,
layer="metal2",
offset=out_pin.scale(1,0),
height=out_pin.y)
out_pin = inst.get_pin(pin_name)
# shift it to the right side of the cell
left_pos=out_pin.center() - vector(out_pin.cx()-inst.lx(),0)
self.add_path("metal1",[out_pin.center(), left_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=left_pos)
self.add_layout_pin_center_segment(text=out_name,
layer="metal2",
start=left_pos.scale(1,0),
end=left_pos)
def add_output_routing(self):
""" Output pin routing """
@ -542,7 +562,7 @@ class control_logic(design.design):
def add_supply_routing(self):
rows_start = self.rail_1_start_x + self.overall_rail_1_gap
rows_end = max(self.row_1_width,self.row_2_width,self.row_3_width)
rows_end = max(self.row_1_end_x,self.row_2_end_x,self.row_3_end_x)
vdd_rail_position = vector(self.rail_1_x_offsets["vdd"], 0)
well_width = drc["minwidth_well"]
@ -615,7 +635,7 @@ class control_logic(design.design):
self.add_path("metal1",[row1_vdd_end_offset,rbl_row1_vdd.lc()])
self.add_path("metal1",[row3_gnd_end_offset,rbl_row3_gnd.lc()])
# row 3 may have a jog due to unequal row heights, so force the full overlap at the end
self.add_path("metal1",[row3_vdd_end_offset - vector(self.m1_pitch,0),row3_vdd_end_offset,rbl_row3_vdd.lc()])
self.add_path("metal1",[row3_vdd_end_offset - vector(self.m1_pitch,0),row3_vdd_end_offset,rbl_row3_vdd.ul()])
# also add a well - around the rail
@ -648,4 +668,11 @@ class control_logic(design.design):
height=pin.height(),
width=pin.width())
pin=self.rbl.get_pin("out")
self.add_label_pin(text="out",
layer="metal1",
offset=pin.ll(),
height=pin.height(),
width=pin.width())

View File

@ -74,8 +74,7 @@ class delay_chain(design.design):
self.inv_list.append([stage_num+1, False])
def add_inv_list(self):
"""add the inverter and connect them based on the stage list """
a_pin = self.inv.get_pin("A")
""" Add the inverters and connect them based on the stage list """
dummy_load_counter = 1
self.inv_inst_list = []
for i in range(self.num_inverters):
@ -84,18 +83,11 @@ class delay_chain(design.design):
# add top level that is upside down
inv_offset = vector(i * self.inv.width, 2 * self.inv.height)
inv_mirror="MX"
via_offset = inv_offset + a_pin.ll().scale(1,-1)
m1m2_via_rotate=270
else:
# add bottom level from right to left
inv_offset = vector((self.num_inverters - i) * self.inv.width, 0)
inv_mirror="MY"
via_offset = inv_offset + a_pin.ll().scale(-1,1)
m1m2_via_rotate=90
self.add_via(layers=("metal1", "via1", "metal2"),
offset=via_offset,
rotate=m1m2_via_rotate)
cur_inv=self.add_inst(name="dinv{}".format(i),
mod=self.inv,
offset=inv_offset,
@ -125,48 +117,45 @@ class delay_chain(design.design):
self.connect_inst(args=[input, output, "vdd", "gnd"])
if i != 0:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=cur_inv.get_pin("A").center())
def add_route(self, pin1, pin2):
""" This guarantees that we route from the top to bottom row correctly. """
pin1_pos = pin1.center()
pin2_pos = pin2.center()
if pin1_pos.y == pin2_pos.y:
self.add_path("metal2", [pin1_pos, pin2_pos])
else:
mid_point = vector(pin2_pos.x, 0.5*(pin1_pos.y+pin2_pos.y))
# Written this way to guarantee it goes right first if we are switching rows
self.add_path("metal2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos])
def route_inv(self):
""" Add metal routing for each of the fanout stages """
start_inv = end_inv = 0
z_pin = self.inv.get_pin("Z")
a_pin = self.inv.get_pin("A")
for fanout in self.fanout_list:
# end inv number depends on the fan out number
end_inv = start_inv + fanout
start_inv_offset = self.inv_inst_list[start_inv].offset
end_inv_offset = self.inv_inst_list[end_inv].offset
if start_inv < self.num_top_half:
start_o_offset = start_inv_offset + z_pin.ll().scale(1,-1)
m1m2_via_rotate = 270
y_dir = -1
else:
start_o_offset = start_inv_offset + z_pin.ll().scale(-1,1)
m1m2_via_rotate = 90
y_dir = 1
M2_start = start_o_offset + vector(0,drc["minwidth_metal2"]).scale(1,y_dir*0.5)
start_inv_inst = self.inv_inst_list[start_inv]
self.add_via(layers=("metal1", "via1", "metal2"),
offset=start_o_offset,
rotate=m1m2_via_rotate)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start_inv_inst.get_pin("Z").center()),
if end_inv < self.num_top_half:
end_i_offset = end_inv_offset + a_pin.ll().scale(1,-1)
M2_end = end_i_offset - vector(0, 0.5 * drc["minwidth_metal2"])
else:
end_i_offset = end_inv_offset + a_pin.ll().scale(-1,1)
M2_end = end_i_offset + vector(0, 0.5 * drc["minwidth_metal2"])
# We need a wire if the routing spans multiple rows
if start_inv < self.num_top_half and end_inv >= self.num_top_half:
mid = vector(self.num_top_half * self.inv.width - 0.5 * drc["minwidth_metal2"],
M2_start[1])
self.add_wire(("metal2", "via2", "metal3"),
[M2_start, mid, M2_end])
else:
self.add_path(("metal2"), [M2_start, M2_end])
# route from output to first load
start_inv_pin = start_inv_inst.get_pin("Z")
load_inst = self.inv_inst_list[start_inv+1]
load_pin = load_inst.get_pin("A")
self.add_route(start_inv_pin, load_pin)
next_inv = start_inv+2
while next_inv <= end_inv:
prev_load_inst = self.inv_inst_list[next_inv-1]
prev_load_pin = prev_load_inst.get_pin("A")
load_inst = self.inv_inst_list[next_inv]
load_pin = load_inst.get_pin("A")
self.add_route(prev_load_pin, load_pin)
next_inv += 1
# set the start of next one after current end
start_inv = end_inv
@ -202,19 +191,19 @@ class delay_chain(design.design):
self.add_path("metal1", [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
# input is A pin of first inverter
a_pin = self.inv.get_pin("A")
first_offset = self.inv_inst_list[0].offset
a_pin = self.inv_inst_list[0].get_pin("A")
self.add_layout_pin(text="in",
layer="metal1",
offset=first_offset+a_pin.ll().scale(1,-1) - vector(0,drc["minwidth_metal1"]))
offset=a_pin.ll(),
width=a_pin.width(),
height=a_pin.height())
# output is Z pin of last inverter
z_pin = self.inv.get_pin("Z")
z_pin = self.inv_inst_list[-1].get_pin("Z")
self.add_layout_pin(text="out",
layer="metal1",
offset=z_pin.ll().scale(0,1),
width=self.inv.width-z_pin.lx())
width=z_pin.lx())

View File

@ -22,6 +22,8 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
self.name = name
hierarchy_layout.layout.__init__(self, name)
hierarchy_spice.spice.__init__(self, name)
self.setup_drc_constants()
# Check if the name already exists, if so, give an error
# because each reference must be a unique name.
@ -36,6 +38,18 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
else:
debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1)
def setup_drc_constants(self):
""" These are some DRC constants used in many places in the compiler."""
from tech import drc
self.poly_width = drc["minwidth_poly"]
self.poly_space = drc["poly_to_poly"]
self.m1_width = drc["minwidth_metal1"]
self.m1_space = drc["metal1_to_metal1"]
self.m2_width = drc["minwidth_metal2"]
self.m2_space = drc["metal2_to_metal2"]
self.m3_width = drc["minwidth_metal3"]
self.m3_space = drc["metal3_to_metal3"]
def get_layout_pins(self,inst):
""" Return a map of pin locations of the instance offset """
# find the instance

View File

@ -124,15 +124,24 @@ class instance(geometry):
""" Return the right edge """
return self.boundary[1].x
def get_pin(self,name):
def get_pin(self,name,index=-1):
""" Return an absolute pin that is offset and transformed based on
this instance location. """
import copy
pin = copy.deepcopy(self.mod.get_pin(name))
pin.transform(self.offset,self.mirror,self.rotate)
return pin
this instance location. Index will return one of several pins."""
import copy
if index==-1:
pin = copy.deepcopy(self.mod.get_pin(name))
pin.transform(self.offset,self.mirror,self.rotate)
return pin
else:
pins = copy.deepcopy(self.mod.get_pin(name))
pin.transform(self.offset,self.mirror,self.rotate)
return pin[index]
def get_num_pins(self, name):
""" Return the number of pins of a given name """
return len(self.mod.get_pins(name))
def get_pins(self,name):
""" Return an absolute pin that is offset and transformed based on
this instance location. """

View File

@ -4,9 +4,9 @@ import design
from math import log
from math import sqrt
import math
from contact import contact
from nand_2 import nand_2
from nand_3 import nand_3
import contact
from pnand2 import pnand2
from pnand3 import pnand3
from pinv import pinv
from hierarchical_predecode2x4 import hierarchical_predecode2x4 as pre2x4
from hierarchical_predecode3x8 import hierarchical_predecode3x8 as pre3x8
@ -51,9 +51,9 @@ class hierarchical_decoder(design.design):
def add_modules(self):
self.inv = pinv()
self.add_mod(self.inv)
self.nand2 = nand_2()
self.nand2 = pnand2()
self.add_mod(self.nand2)
self.nand3 = nand_3()
self.nand3 = pnand3()
self.add_mod(self.nand3)
# CREATION OF PRE-DECODER
@ -85,12 +85,11 @@ class hierarchical_decoder(design.design):
debug.error("Invalid number of inputs for hierarchical decoder",-1)
def setup_layout_constants(self):
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
# Vertical metal rail gap definition
self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2
self.metal2_spacing = self.metal2_extend_contact + drc["metal2_to_metal2"]
self.metal2_pitch = self.metal2_spacing + drc["minwidth_metal2"]
self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2
self.metal2_extend_contact = (contact.m1m2.second_layer_height - contact.m1m2.contact_width) / 2
self.metal2_spacing = self.metal2_extend_contact + self.m2_space
self.metal2_pitch = self.metal2_spacing + self.m2_width
self.via_shift = (contact.m1m2.second_layer_width - contact.m1m2.first_layer_width) / 2
self.predec_groups = [] # This array is a 2D array.
@ -262,7 +261,9 @@ class hierarchical_decoder(design.design):
and add the primary decoder output pins. """
if (self.num_inputs >= 4):
self.add_decoder_nand_array()
self.add_decoder_inv_array_and_pins()
self.add_decoder_inv_array()
self.route_decoder()
def add_decoder_nand_array(self):
""" Add a column of NAND gates for final decode """
@ -299,36 +300,26 @@ class hierarchical_decoder(design.design):
def add_nand_array(self, nand_mod, correct=0):
""" Add a column of NAND gates for the decoder above the predecoders."""
z_pin = nand_mod.get_pin("Z")
a_pin = self.inv.get_pin("A")
rect_height = z_pin.uy()-a_pin.by()
self.nand_inst = []
for row in range(self.rows):
name = "DEC_NAND[{0}]".format(row)
if ((row % 2) == 0):
y_off = self.predecoder_height + nand_mod.height*row
y_dir = 1
mirror = "R0"
rect_offset = vector(self.routing_width + nand_mod.width, y_off + z_pin.uy() - rect_height)
else:
y_off = self.predecoder_height + nand_mod.height*(row + 1)
y_dir = -1
mirror = "MX"
rect_offset =vector(self.routing_width + nand_mod.width, y_off - z_pin.uy())
self.add_inst(name=name,
mod=nand_mod,
offset=[self.routing_width, y_off],
mirror=mirror)
self.nand_inst.append(self.add_inst(name=name,
mod=nand_mod,
offset=[self.routing_width, y_off],
mirror=mirror))
self.add_rect(layer="metal1",
offset=rect_offset,
width=drc["minwidth_metal1"],
height=rect_height)
def add_decoder_inv_array_and_pins(self):
def add_decoder_inv_array(self):
"""Add a column of INV gates for the decoder above the predecoders
and to the right of the NAND decoders."""
@ -338,7 +329,8 @@ class hierarchical_decoder(design.design):
x_off = self.routing_width + self.nand2.width
else:
x_off = self.routing_width + self.nand3.width
self.inv_inst = []
for row in range(self.rows):
name = "DEC_INV_[{0}]".format(row)
if (row % 2 == 0):
@ -352,22 +344,38 @@ class hierarchical_decoder(design.design):
y_off = self.predecoder_height + inv_row_height
offset = vector(x_off,y_off)
self.add_inst(name=name,
mod=self.inv,
offset=offset,
mirror=mirror)
self.inv_inst.append(self.add_inst(name=name,
mod=self.inv,
offset=offset,
mirror=mirror))
# This will not check that the inst connections match.
self.connect_inst(args=["Z[{0}]".format(row),
"decode[{0}]".format(row),
"vdd", "gnd"],
check=False)
def route_decoder(self):
""" Route the nand to inverter in the decoder and add the pins. """
for row in range(self.rows):
# route nand output to output inv input
zr_pos = self.nand_inst[row].get_pin("Z").rc()
al_pos = self.inv_inst[row].get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pin = self.inv_inst[row].get_pin("Z")
self.add_layout_pin(text="decode[{0}]".format(row),
layer="metal1",
offset=offset+z_pin.ll().scale(1,y_dir),
offset=z_pin.ll(),
width=z_pin.width(),
height=y_dir*z_pin.height())
height=z_pin.height())
def create_vertical_rail(self):
@ -382,7 +390,7 @@ class hierarchical_decoder(design.design):
# The offsets go into the negative x direction
# assuming the predecodes are placed at (self.routing_width,0)
x_offset = self.metal2_pitch * i
self.rail_x_offsets.append(x_offset)
self.rail_x_offsets.append(x_offset+0.5*self.m2_width)
self.add_rect(layer="metal2",
offset=vector(x_offset,0),
width=drc["minwidth_metal2"],
@ -394,46 +402,22 @@ class hierarchical_decoder(design.design):
def connect_rails_to_predecodes(self):
""" Iterates through all of the predecodes and connects to the rails including the offsets """
for i in range(self.no_of_pre2x4):
self.connect_rails_to_pre2x4(i)
for pre_num in range(self.no_of_pre2x4):
for i in range(4):
index = pre_num * 4 + i
out_name = "out[{}]".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.connect_rail(index, pin)
for i in range(self.no_of_pre3x8):
self.connect_rails_to_pre3x8(i)
def connect_rails_to_pre2x4(self, predecode_num):
""" Connects the 2x4 predecoder outputs to the vertical rails """
z_pin = self.inv.get_pin("Z")
pin = z_pin.ll()
for i in range(4):
index = predecode_num * 4 + i
current_inv_height = predecode_num*self.pre2_4.height + i*self.inv.height
if (i % 2 == 0):
pin_y = pin.y
else:
pin_y = self.inv.height - drc["minwidth_metal1"] - pin.y
self.connect_rail(vector(self.rail_x_offsets[index], current_inv_height + pin_y))
def connect_rails_to_pre3x8(self, predecode_num):
""" Connects the 3x8 predecoder outputs to the vertical rails """
z_pin = self.inv.get_pin("Z")
pin = z_pin.ll()
for i in range(8):
index = predecode_num * 8 + i + self.no_of_pre2x4 * 4
current_inv_height = predecode_num*self.pre3_8.height \
+ i*self.inv.height \
+ self.no_of_pre2x4*self.pre2_4.height
if (i % 2 == 0):
pin_y = pin.y
else:
pin_y = self.inv.height - drc["minwidth_metal1"] - pin.y
for pre_num in range(self.no_of_pre3x8):
for i in range(8):
index = pre_num * 8 + i + self.no_of_pre2x4 * 4
out_name = "out[{}]".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
self.connect_rail(index, pin)
self.connect_rail(vector(self.rail_x_offsets[index], current_inv_height + pin_y))
def connect_rails_to_decoder(self):
""" Use the self.predec_groups to determine the connections to the decoder NAND gates.
@ -445,53 +429,21 @@ class hierarchical_decoder(design.design):
"""
row_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5):
a_pin = self.nand2.get_pin("A")
b_pin = self.nand2.get_pin("B")
for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]:
current_inv_height = self.predecoder_height + row_index*self.inv.height
if (row_index % 2 == 0):
yoffset_A = current_inv_height + a_pin.by()
yoffset_B = current_inv_height + b_pin.by()
else:
base = current_inv_height + self.inv.height - drc["minwidth_metal1"]
yoffset_A = base - a_pin.by()
yoffset_B = base - b_pin.by()
self.connect_rail(index_A, self.nand_inst[row_index].get_pin("A"))
self.connect_rail(index_B, self.nand_inst[row_index].get_pin("B"))
row_index = row_index + 1
self.connect_rail(vector(self.rail_x_offsets[index_A], yoffset_A))
self.connect_rail(vector(self.rail_x_offsets[index_B], yoffset_B))
elif (self.num_inputs > 5):
a_pin = self.nand3.get_pin("A")
b_pin = self.nand3.get_pin("B")
c_pin = self.nand3.get_pin("C")
for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]:
for index_C in self.predec_groups[2]:
current_inv_height = self.predecoder_height + row_index*self.inv.height
if (row_index % 2 == 0):
yoffset_A = current_inv_height + a_pin.by()
yoffset_B = current_inv_height + b_pin.by()
yoffset_C = current_inv_height + c_pin.by()
else:
base = current_inv_height + self.inv.height - drc["minwidth_metal1"]
yoffset_A = base - a_pin.by()
yoffset_B = base - b_pin.by()
yoffset_C = base - c_pin.by()
self.connect_rail(index_A, self.nand_inst[row_index].get_pin("A"))
self.connect_rail(index_B, self.nand_inst[row_index].get_pin("B"))
self.connect_rail(index_C, self.nand_inst[row_index].get_pin("C"))
row_index = row_index + 1
self.connect_rail(vector(self.rail_x_offsets[index_A], yoffset_A))
self.connect_rail(vector(self.rail_x_offsets[index_B], yoffset_B))
self.connect_rail(vector(self.rail_x_offsets[index_C], yoffset_C))
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
@ -517,20 +469,15 @@ class hierarchical_decoder(design.design):
height=drc["minwidth_metal1"])
def connect_rail(self, offset,contact_yoffset=0):
""" Adds a via at location and extends to self.routing_width """
self.add_rect(layer="metal1",
offset=offset,
width=self.routing_width-offset.x,
height=drc["minwidth_metal1"])
if contact_yoffset!=0:
yoffset = contact_yoffset
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset + vector(self.metal2_spacing,-self.via_shift),
rotate=90)
def connect_rail(self, rail_index, pin):
""" Connect the routing rail to the given metal1 pin """
rail_pos = vector(self.rail_x_offsets[rail_index],pin.lc().y)
self.add_path("metal1", [rail_pos, pin.lc()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos,
rotate=90)
def analytical_delay(self, slew, load = 0.0):
# A -> out
if self.determine_predecodes(self.num_inputs)[1]==0:

View File

@ -2,12 +2,12 @@ import debug
import design
import math
from tech import drc
from contact import contact
import contact
from pinv import pinv
from vector import vector
from globals import OPTS
from nand_2 import nand_2
from nand_3 import nand_3
from pnand2 import pnand2
from pnand3 import pnand3
class hierarchical_predecode(design.design):
@ -44,46 +44,37 @@ class hierarchical_predecode(design.design):
def create_nand(self,inputs):
""" Create the NAND for the predecode input stage """
if inputs==2:
self.nand = nand_2()
self.nand = pnand2()
elif inputs==3:
self.nand = nand_3()
self.nand = pnand3()
else:
debug.error("Invalid number of predecode inputs.",-1)
def setup_constraints(self):
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.metal2_space = drc["metal2_to_metal2"]
self.metal1_space = drc["metal1_to_metal1"]
self.metal2_width = drc["minwidth_metal2"]
self.metal1_width = drc["minwidth_metal1"]
# we are going to use horizontal vias, so use the via height
# use a conservative douple spacing just to get rid of annoying via DRCs
self.metal2_pitch = self.m1m2_via.height + 2*self.metal2_space
# This is to shift the rotated vias to be on m2 pitch
self.via_x_shift = self.m1m2_via.height + self.m1m2_via.via_layer_position.scale(0,-1).y
# This is to shift the via if the metal1 and metal2 overlaps are different
self.via_y_shift = self.m1m2_via.second_layer_position.x - self.m1m2_via.first_layer_position.x + self.m1m2_via.via_layer_position.scale(-0.5,0).x
self.m2_pitch = contact.m1m2.height + 2*self.m2_space
# The rail offsets are indexed by the label
self.rails = {}
# Non inverted input rails
for rail_index in range(self.number_of_inputs):
xoffset = rail_index * self.metal2_pitch
xoffset = rail_index * self.m2_pitch + 0.5*self.m2_width
self.rails["in[{}]".format(rail_index)]=xoffset
# x offset for input inverters
self.x_off_inv_1 = self.number_of_inputs*self.metal2_pitch
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
# Creating the right hand side metal2 rails for output connections
for rail_index in range(2 * self.number_of_inputs):
xoffset = self.x_off_inv_1 + self.inv.width + ((rail_index+1) * self.metal2_pitch)
xoffset = self.x_off_inv_1 + self.inv.width + ((rail_index+1) * self.m2_pitch) + 0.5*self.m2_width
if rail_index < self.number_of_inputs:
self.rails["Abar[{}]".format(rail_index)]=xoffset
else:
self.rails["A[{}]".format(rail_index-self.number_of_inputs)]=xoffset
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus an extra m2 pitch
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (1 + 2*self.number_of_inputs) * self.metal2_pitch
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (1 + 2*self.number_of_inputs) * self.m2_pitch
# x offset to output inverters
@ -101,17 +92,19 @@ class hierarchical_predecode(design.design):
if label.startswith("in"):
self.add_layout_pin(text=label,
layer="metal2",
offset=[self.rails[label], 0],
width=self.metal2_width,
height=self.height - drc["metal2_to_metal2"])
offset=vector(self.rails[label] - 0.5*self.m2_width, 0),
width=self.m2_width,
height=self.height - 2*self.m2_space)
else:
self.add_rect(layer="metal2",
offset=[self.rails[label], 0],
width=self.metal2_width,
height=self.height - drc["metal2_to_metal2"])
offset=vector(self.rails[label] - 0.5*self.m2_width, 0),
width=self.m2_width,
height=self.height - 2*self.m2_space)
def add_input_inverters(self):
""" Create the input inverters to invert input signals for the decode stage. """
self.in_inst = []
for inv_num in range(self.number_of_inputs):
name = "Xpre_inv[{0}]".format(inv_num)
if (inv_num % 2 == 0):
@ -121,10 +114,10 @@ class hierarchical_predecode(design.design):
y_off = (inv_num + 1) * (self.inv.height)
mirror="MX"
offset = vector(self.x_off_inv_1, y_off)
self.add_inst(name=name,
mod=self.inv,
offset=offset,
mirror=mirror)
self.in_inst.append(self.add_inst(name=name,
mod=self.inv,
offset=offset,
mirror=mirror))
self.connect_inst(["in[{0}]".format(inv_num),
"inbar[{0}]".format(inv_num),
"vdd", "gnd"])
@ -133,7 +126,6 @@ class hierarchical_predecode(design.design):
""" Create inverters for the inverted output decode signals. """
self.inv_inst = []
z_pin = self.inv.get_pin("Z")
for inv_num in range(self.number_of_outputs):
name = "Xpre_nand_inv[{}]".format(inv_num)
if (inv_num % 2 == 0):
@ -151,12 +143,6 @@ class hierarchical_predecode(design.design):
"out[{}]".format(inv_num),
"vdd", "gnd"])
z_pin = self.inv_inst[-1].get_pin("Z")
self.add_layout_pin(text="out[{}]".format(inv_num),
layer="metal1",
offset=z_pin.ll(),
width=z_pin.width(),
height=z_pin.height())
def add_nand(self,connections):
@ -177,22 +163,13 @@ class hierarchical_predecode(design.design):
offset=offset,
mirror=mirror))
self.connect_inst(connections[nand_input])
z_pin = self.nand_inst[nand_input].get_pin("Z")
a_pin = self.inv_inst[nand_input].get_pin("A")
y_min = min(z_pin.by(),a_pin.by())
y_max = max(z_pin.uy(),a_pin.uy())
offset = vector(z_pin.rx(),y_min)
self.add_rect(layer="metal1",
offset=offset,
width=self.metal1_width,
height=y_max-y_min)
def route(self):
self.route_input_inverters()
self.route_inputs_to_rails()
self.route_nand_to_rails()
self.route_output_inverters()
self.route_vdd_gnd()
def route_inputs_to_rails(self):
@ -201,57 +178,68 @@ class hierarchical_predecode(design.design):
# route one signal next to each vdd/gnd rail since this is
# typically where the p/n devices are and there are no
# pins in the nand gates.
y_offset = (num+self.number_of_inputs) * self.inv.height + 2*self.metal1_space
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1m2.width + self.m1_space
in_pin = "in[{}]".format(num)
a_pin = "A[{}]".format(num)
self.add_rect(layer="metal1",
offset=[self.rails[in_pin],y_offset],
width=self.rails[a_pin] + self.metal2_width - self.rails[in_pin],
height=self.metal1_width)
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[in_pin] + self.via_x_shift, y_offset + self.via_y_shift],
rotate=90)
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[a_pin] + self.via_x_shift, y_offset + self.via_y_shift],
rotate=90)
a_pin = "A[{}]".format(num)
in_pos = vector(self.rails[in_pin],y_offset)
a_pos = vector(self.rails[a_pin],y_offset)
self.add_path("metal1",[in_pos, a_pos])
self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[in_pin], y_offset],
rotate=90)
self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[a_pin], y_offset],
rotate=90)
def route_output_inverters(self):
"""
Route all conections of the outputs inverters
"""
for num in range(self.number_of_outputs):
# route nand output to output inv input
zr_pos = self.nand_inst[num].get_pin("Z").rc()
al_pos = self.inv_inst[num].get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pos = self.inv_inst[num].get_pin("Z").rc()
self.add_layout_pin_center_segment(text="out[{}]".format(num),
layer="metal1",
start=z_pos,
end=z_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(),0))
def route_input_inverters(self):
"""
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
"""
for inv_num in range(self.number_of_inputs):
(inv_offset, y_dir) = self.get_gate_offset(self.x_off_inv_1, self.inv.height, inv_num)
out_pin = "Abar[{}]".format(inv_num)
in_pin = "in[{}]".format(inv_num)
#add output so that it is just below the vdd or gnd rail
# since this is where the p/n devices are and there are no
# pins in the nand gates.
y_offset = (inv_num+1) * self.inv.height - 3*self.metal1_space
inv_out_offset = inv_offset+self.inv.get_pin("Z").ur().scale(1,y_dir)-vector(0,self.metal1_width).scale(1,y_dir)
self.add_rect(layer="metal1",
offset=[inv_out_offset.x,y_offset],
width=self.rails[out_pin]-inv_out_offset.x + self.metal2_width,
height=self.metal1_width)
self.add_rect(layer="metal1",
offset=inv_out_offset,
width=self.metal1_width,
height=y_offset-inv_out_offset.y)
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[out_pin] + self.via_x_shift, y_offset + self.via_y_shift],
rotate=90)
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
rail_pos = vector(self.rails[out_pin],y_offset)
self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_center(layers = ("metal1", "via1", "metal2"),
offset=rail_pos,
rotate=90)
#route input
inv_in_offset = inv_offset+self.inv.get_pin("A").ll().scale(1,y_dir)
self.add_rect(layer="metal1",
offset=[self.rails[in_pin], inv_in_offset.y],
width=inv_in_offset.x - self.rails[in_pin],
height=self.metal1_width)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails[in_pin] + self.via_x_shift, inv_in_offset.y + self.via_y_shift],
rotate=90)
inv_in_pos = self.in_inst[inv_num].get_pin("A").lc()
in_pos = vector(self.rails[in_pin],inv_in_pos.y)
self.add_path("metal1", [in_pos, inv_in_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=in_pos,
rotate=90)
def route_nand_to_rails(self):
@ -260,7 +248,6 @@ class hierarchical_predecode(design.design):
for k in range(self.number_of_outputs):
# create x offset list
index_lst= nand_input_line_combination[k]
(nand_offset,y_dir) = self.get_gate_offset(self.x_off_nand,self.nand.height,k)
if self.number_of_inputs == 2:
gate_lst = ["A","B"]
@ -269,14 +256,12 @@ class hierarchical_predecode(design.design):
# this will connect pins A,B or A,B,C
for rail_pin,gate_pin in zip(index_lst,gate_lst):
pin_offset = nand_offset+self.nand.get_pin(gate_pin).ll().scale(1,y_dir)
self.add_rect(layer="metal1",
offset=[self.rails[rail_pin], pin_offset.y],
width=pin_offset.x - self.rails[rail_pin],
height=self.metal1_width)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails[rail_pin] + self.via_x_shift, pin_offset.y + self.via_y_shift],
rotate=90)
pin_pos = self.nand_inst[k].get_pin(gate_pin).lc()
rail_pos = vector(self.rails[rail_pin], pin_pos.y)
self.add_path("metal1", [rail_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos,
rotate=90)
@ -290,20 +275,18 @@ class hierarchical_predecode(design.design):
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
# route vdd
vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir)
vdd_offset = self.nand_inst[num].get_pin("vdd").ll().scale(0,1)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_offset,
width=self.x_off_inv_2 + self.inv.width + self.metal2_width,
height=self.metal1_width)
width=self.inv_inst[num].rx())
# route gnd
gnd_offset = gate_offset+self.inv.get_pin("gnd").ll().scale(1,y_dir)
gnd_offset = self.nand_inst[num].get_pin("gnd").ll().scale(0,1)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_offset,
width=self.x_off_inv_2 + self.inv.width + self.metal2_width,
height=self.metal1_width)
width=self.inv_inst[num].rx())

View File

@ -383,6 +383,7 @@ class layout:
return mos
def gds_read(self):
"""Reads a GDSII file in the library and checks if it exists
Otherwise, start a new layout for dynamic generation."""

View File

@ -1,425 +0,0 @@
import contact
import design
import debug
from tech import drc, parameter, spice
from ptx import ptx
from vector import vector
from globals import OPTS
class nand_2(design.design):
"""
This module generates gds of a parametrically sized 2_input nand.
This model use ptx to generate a 2_input nand within a cetrain height.
The 2_input nand's cell_height should be the same as the 6t library cell
This module doesn't generate multi_finger 2_input nand gate,
It generate only the minimum size 2_input nand gate.
Creates a pcell for a simple 2_input nand
"""
c = reload(__import__(OPTS.config.bitcell))
bitcell = getattr(c, OPTS.config.bitcell)
unique_id = 1
def __init__(self, size=2, height=bitcell.height):
"""Constructor : Creates a cell for a simple 2 input nand"""
name = "nand2_{0}".format(nand_2.unique_id)
nand_2.unique_id += 1
design.design.__init__(self, name)
debug.info(2, "create nand_2 structure {0} with size of {1}".format(name, nmos_width))
self.nmos_size = 2*size
# FIXME why is this?
self.pmos_size = size
self.tx_mults = 1
self.height = height
self.add_pins()
self.create_layout()
#self.DRC_LVS()
def add_pins(self):
""" Add pins """
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
def create_layout(self):
""" Layout """
self.create_ptx()
self.setup_layout_constants()
self.add_rails()
self.add_ptx()
self.add_well_contacts()
# This isn't instantiated, but we use it to get the dimensions
self.poly_contact = contact.contact(("poly", "contact", "metal1"))
self.connect_well_contacts()
self.connect_rails()
self.connect_tx()
self.route_pins()
self.extend_wells()
self.extend_active()
# transistors are created here but not yet placed or added as a module
def create_ptx(self):
""" Add required modules """
self.nmos = ptx(width=self.nmos_size,
mults=self.tx_mults,
tx_type="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)
def setup_layout_constants(self):
""" Calculate the layout constraints """
self.well_width = self.pmos.active_position.x \
+ 2 * self.pmos.active_width \
+ drc["active_to_body_active"] + \
drc["well_enclosure_active"]
self.width = self.well_width
def add_rails(self):
""" add power rails """
rail_width = self.width
rail_height = drc["minwidth_metal1"]
self.rail_height = rail_height
# 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)
edge_to_nmos = max(drc["metal1_to_metal1"]
- self.nmos.active_contact_positions[0].y,
0.5 * (drc["poly_to_poly"] - drc["minwidth_metal1"])
- self.nmos.poly_positions[0].y)
# determine the position of the first transistor from the left
self.nmos_position1 = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
offset = self.nmos_position1+ vector(0,self.nmos.height)
self.add_inst(name="nmos1",
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["Z", "A", "net1", "gnd"])
self.nmos_position2 = vector(self.nmos.active_width - self.nmos.active_contact.width,
self.nmos_position1.y)
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", "gnd", "gnd"])
# determines the spacing between the edge and pmos
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(0, self.height - 0.5 * drc["minwidth_metal1"]
- edge_to_pmos - self.pmos.height)
self.add_inst(name="pmos1",
mod=self.pmos,
offset=self.pmos_position1)
self.connect_inst(["vdd", "A", "Z", "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(["Z", "B", "vdd", "vdd"])
def add_well_contacts(self):
""" Create and add well copntacts """
# create well contacts
layer_stack = ("active", "contact", "metal1")
xoffset = (self.nmos_position2.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)
offset = self.nwell_contact_position = vector(xoffset, yoffset)
self.pwell_contact=self.add_contact(layer_stack,offset,(1,self.nmos.num_contacts))
xoffset = (self.nmos_position2.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)
offset = self.pwell_contact_position = vector(xoffset, yoffset)
self.nwell_contact=self.add_contact(layer_stack,offset,(1,self.pmos.num_contacts))
def connect_well_contacts(self):
""" Connect well contacts to vdd and gnd rail """
well_tap_length = (self.height - self.nwell_contact_position.y)
offset = vector(self.nwell_contact_position.x
+ self.nwell_contact.second_layer_position.x
- self.nwell_contact.first_layer_position.x,
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 = (self.pwell_contact_position.scale(1,0)
+ self.pwell_contact.second_layer_position.scale(1,0)
- 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(.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 = vector(2 * self.pmos_position2.x + correct.x
+ self.pmos.active_contact_positions[0].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):
""" Connect tx poly znd drains """
self.connect_poly()
self.connect_drains()
def connect_poly(self):
""" poly connection """
yoffset_nmos1 = (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_nmos1 + drc["minwidth_poly"])
for position in self.pmos.poly_positions:
offset = vector(position.x,
yoffset_nmos1 - 0.5 * 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)
def connect_drains(self):
""" Connect pmos and nmos drains. The output will be routed to this connection point. """
yoffset = self.nmos_position1.y + self.nmos.active_contact_positions[0].y
drain_length = (self.height + self.pmos.active_contact_positions[0].y
- yoffset - self.pmos.height + 0.5 * drc["minwidth_metal2"])
for position in self.pmos.active_contact_positions[1:][::2]:
start = self.drain_position = vector(position.x + 0.5 * drc["minwidth_metal1"]
+ self.pmos_position2.x
+ self.pmos.active_contact.first_layer_position.x
+ self.pmos.active_contact.width / 2,
yoffset)
mid1 = vector(start.x,
self.height - drc["minwidth_metal2"] - drc["metal2_to_metal2"] -
self.pmos_size - drc["metal1_to_metal1"] - 0.5 * drc["minwidth_metal1"])
end = vector(position.x + 0.5 * drc["minwidth_metal1"]
+ self.pmos.active_contact.second_layer_position.x,
self.pmos_position1.y + self.pmos.active_contact_positions[0].y)
mid2 = vector(end.x, mid1.y)
self.add_path("metal1",[start, mid1, mid2, end])
def route_pins(self):
""" Routing """
self.route_input_gate()
self.route_output()
def route_input_gate(self):
""" Gate routing """
self.route_input_gate_A()
self.route_input_gate_B()
def route_input_gate_A(self):
""" routing for input A """
xoffset = self.pmos.poly_positions[0].x
yoffset = (self.height
- (drc["minwidth_metal1"]
+ drc["metal1_to_metal1"]
+ self.pmos.active_height
+ drc["metal1_to_metal1"]
+ self.pmos.active_contact.second_layer_width))
if (self.nmos_size == drc["minwidth_tx"]):
yoffset = (self.pmos_position1.y
+ self.pmos.poly_positions[0].y
+ drc["poly_extend_active"]
- (self.pmos.active_contact.height
- self.pmos.active_height) / 2
- drc["metal1_to_metal1"]
- self.poly_contact.width)
offset = [xoffset, yoffset]
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
size=(1,1),
rotate=90)
offset = offset - self.poly_contact.first_layer_position.rotate_scale(1,0)
self.add_rect(layer="poly",
offset=offset,
width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
height=self.poly_contact.first_layer_width)
input_length = (self.pmos.poly_positions[0].x
- self.poly_contact.height)
yoffset += self.poly_contact.via_layer_position.x
offset = self.input_position1 = vector(0, yoffset)
self.add_layout_pin(text="A",
layer="metal1",
offset=offset,
width=input_length,
height=drc["minwidth_metal1"])
def route_input_gate_B(self):
""" routing for input B """
xoffset = (self.pmos.poly_positions[0].x
+ self.pmos_position2.x + drc["minwidth_poly"])
yoffset = (drc["minwidth_metal1"]
+ drc["metal1_to_metal1"]
+ self.nmos.active_height
+ drc["minwidth_metal1"])
if (self.nmos_size == drc["minwidth_tx"]):
yoffset = (self.nmos_position1.y
+ self.nmos.poly_positions[0].y
+ self.nmos.poly_height
+ drc["metal1_to_metal1"])
offset = [xoffset, yoffset]
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
size=(1,1),
rotate=90)
input_length = self.pmos.poly_positions[0].x - self.poly_contact.height
input_position2 = vector(xoffset - self.poly_contact.width,
yoffset + self.poly_contact.via_layer_position.x)
self.add_layout_pin(text="B",
layer="metal1",
offset=input_position2.scale(0,1),
width=(input_length + self.pmos_position2.x + drc["minwidth_poly"]),
height=drc["minwidth_metal1"])
def route_output(self):
""" routing for output Z """
yoffset = (self.nmos.height - 2 * drc["minwidth_metal1"] / 3 +
(self.height - self.pmos.height - self.nmos.height - drc["minwidth_metal1"]) / 2 )
xoffset = self.drain_position.x
offset = vector(xoffset, yoffset)
output_length = self.width - xoffset
self.add_layout_pin(text="Z",
layer="metal1",
offset=offset,
width=output_length,
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_position2.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 = vector(self.nmos_position2.x + self.nmos.active_position.x,
self.nmos_position1.y - self.nmos.active_height
- self.nmos.active_position.y + self.nmos.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):
from tech import spice
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_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)

View File

@ -1,441 +0,0 @@
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, size=1, 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 = 3*size
# FIXME: Why is this??
self.pmos_size = 2*size
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"""
# If full contacts, this will interfere with the C input in SCMOS
self.nmos = ptx(width=self.nmos_size,
mults=self.tx_mults,
tx_type="nmos",
num_contacts=1)
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_size,
mults=self.tx_mults,
tx_type="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_contacts))
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_contacts))
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 analytical_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)

View File

@ -1,468 +0,0 @@
import contact
import design
import debug
from tech import drc
from ptx import ptx
from vector import vector
from globals import OPTS
class nor_2(design.design):
"""
This module generates gds of a parametrically sized 2_input nor.
This model use ptx to generate a 2_input nor within a cetrain height.
The 2_input nor cell_height should be the same as the 6t library cell.
If pmos can not fit in the given vertical space, it will be folded
based so that it takes minmium horiztonal space.
"""
c = reload(__import__(OPTS.config.bitcell))
bitcell = getattr(c, OPTS.config.bitcell)
unique_id = 1
def __init__(self, size=1, height=bitcell.height):
"""Constructor : Creates a cell for a simple 2 input nor"""
name = "nor2_{0}".format(nor_2.unique_id)
nor_2.unique_id += 1
design.design.__init__(self, name)
debug.info(2, "create nor_2 structure {0} with size of {1}".format(name, nmos_width))
debug.check(nmos_width==drc["minwidth_tx"], "Need to rewrite nor2 for sizing.")
self.nmos_size =
self.height = height
self.add_pins()
self.create_layout()
#self.DRC_LVS()
def add_pins(self):
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
def create_layout(self):
# 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.determine_sizes()
self.create_modules()
# These aren't for instantiating, but we use them to get the dimensions
self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.pmos.num_contacts))
self.pwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.nmos.num_contacts))
self.setup_layout_constants()
self.add_rails()
self.add_ptx()
self.add_well_contacts()
self.extend_wells()
self.extend_active()
self.route()
def determine_sizes(self, beta=4):
"""Determine transistor size"""
nmos_mults = 1
for pmos_mults in range(1, 5):
nmos_size = self.nmos_width
pmos_size = 3 * self.nmos_width / pmos_mults
test_nmos = ptx(width=nmos_size,
mults=nmos_mults,
tx_type="nmos")
test_pmos = ptx(width=pmos_size,
mults=pmos_mults,
tx_type="nmos")
# FIXME: This is a hack because the old code sucked and didn't work.
# We will rewrite the entire module soon.
# compute the remaining space roughly including rails and tx heights
track_space = self.height - test_nmos.height + test_pmos.height - drc["metal1_to_metal1"] - drc["minwidth_metal1"]
# 3 contacted track space
if track_space > 3*(self.m1m2_via.height + drc["metal1_to_metal1"]):
break
self.nmos_size = nmos_size
self.pmos_size = pmos_size
self.nmos_mults = nmos_mults
self.pmos_mults = pmos_mults
def create_modules(self):
"""transistors are created as modules"""
self.nmos = ptx(width=self.nmos_size,
mults=self.nmos_mults,
tx_type="nmos")
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_size,
mults=self.pmos_mults,
tx_type="pmos")
self.add_mod(self.pmos)
def setup_layout_constants(self):
""" calcuate the transistor spacing and cell size"""
# determines the spacing between the edge and nmos (rail to active
# metal or poly_to_poly spacing)
half_gate_to_gate = 0.5 * (drc["poly_to_poly"] - drc["minwidth_metal1"])
edge_to_nmos = max(drc["metal1_to_metal1"] - self.nmos.active_contact_positions[0].y,
half_gate_to_gate - self.nmos.poly_positions[0].y)
# determine the position of the first transistor from the left
self.nmos_position1 = vector(0,
0.5 * drc["minwidth_metal1"] + edge_to_nmos)
offset = self.nmos_position1 + vector(0,self.nmos.height)
x = vector(self.nmos.active_width - self.nmos.active_contact.width, 0)
self.nmos_position2 = x + self.nmos_position1.scale(0,1)
# determines the spacing between the edge and pmos
edge_to_pmos = max(drc["metal1_to_metal1"] - self.pmos.active_contact_positions[0].y,
half_gate_to_gate - self.pmos.poly_positions[0].y)
self.pmos_position1 = vector(0,
self.height - 0.5 * drc["minwidth_metal1"]
- edge_to_pmos - self.pmos.height)
self.pmos_position2 = self.pmos_position1 + vector(self.pmos.width,0)
self.well_width = max(self.pmos_position2.x + self.pmos.active_position.x
+ self.pmos.active_width
+ drc["active_to_body_active"] + self.nwell_contact.width
+ drc["well_enclosure_active"],
self.nmos_position2.x + self.nmos.active_position.x
+ self.nmos.active_width
+ drc["active_to_body_active"] + drc["well_enclosure_active"])
self.width = self.well_width
def add_rails(self):
rail_width = self.width
rail_height = drc["minwidth_metal1"]
self.rail_height = rail_height
# Relocate the origin
self.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"])
self.add_layout_pin(text="gnd",
layer="metal1",
offset=self.gnd_position,
width=rail_width,
height=rail_height)
self.vdd_position = self.gnd_position + vector(0, self.height)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=self.vdd_position,
width=rail_width,
height=rail_height)
def add_ptx(self):
""" transistors are placed in the layout"""
offset = self.nmos_position1 + vector(0, self.nmos.height)
self.add_inst(name="nmos1",
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["Z", "A", "gnd", "gnd"])
offset = self.nmos_position2 + vector(0, self.nmos.height)
self.add_inst(name="nmos2",
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["Z", "B", "gnd", "gnd"])
offset = self.pmos_position1
self.add_inst(name="pmos1",
mod=self.pmos,
offset=offset)
self.connect_inst(["vdd", "A", "net1", "vdd"])
offset = self.pmos_position2
self.add_inst(name="pmos2",
mod=self.pmos,
offset=offset)
self.connect_inst(["net1", "B", "Z", "vdd"])
def add_well_contacts(self):
layer_stack = ("active", "contact", "metal1")
xoffset = (self.pmos_position2.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_contacts))
xoffset = self.nmos_position2.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_contacts))
def route(self):
self.route_pins()
self.connect_well_contacts()
M1_track = self.B_position.y + max(drc["minwidth_metal2"], self.poly_contact.second_layer_width) + drc["metal2_to_metal2"]
self.connect_tx(M1_track)
self.connect_poly()
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)
offset = (self.pwell_contact_position.scale(1,0)
+ self.pwell_contact.second_layer_position.scale(1,0)
- self.pwell_contact.first_layer_position.scale(1,0))
well_tap_length = self.pwell_contact_position.y
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=well_tap_length)
def connect_tx(self, M1_track):
"""Connect transistor pmos drains to vdd and nmos drains to gnd rail"""
# the first pmos drain to Vdd
for i in range(len(self.pmos.active_contact_positions)):
contact_pos = self.pmos_position1 + self.pmos.active_contact_positions[i]
if i % 2 == 0:
correct = self.pmos.active_contact.second_layer_position.scale(1,0)
drain_posistion = contact_pos + correct
height = self.vdd_position.y - drain_posistion.y
self.add_rect(layer="metal1",
offset=drain_posistion,
width=drc["minwidth_metal1"],
height=height)
else:
# source to pmos2
correct = (self.pmos.active_contact.second_layer_position.scale(1,0)
+ vector(self.pmos.active_contact.second_layer_width,
0).scale(0.5,0))
source_position = contact_pos + correct
mid = [self.pmos_position2.x, M1_track]
self.add_path("metal1", [source_position, mid])
# the second pmos
for i in range(len(self.pmos.active_contact_positions)):
if i % 2 == 0:
# source to pmos2
pmos_active =self.pmos_position2+self.pmos.active_contact_positions[i]
correct= (self.pmos.active_contact.second_layer_position.scale(1,0)
+ vector(0.5 * self.pmos.active_contact.second_layer_width,0))
source_position = pmos_active + correct
mid = [self.pmos_position2.x, M1_track]
self.add_path("metal1", [source_position, mid])
# two nmos source to gnd
source_posistion1 = (self.nmos_position1
+ self.nmos.active_contact_positions[0]
+ self.nmos.active_contact.second_layer_position.scale(1,0))
height = self.gnd_position.y - source_posistion1.y
self.add_rect(layer="metal1",
offset=source_posistion1,
width=drc["minwidth_metal1"],
height=height)
source_posistion2 = (self.nmos_position2
+ self.nmos.active_contact_positions[1]
+ self.nmos.active_contact.second_layer_position.scale(1,0))
height = self.gnd_position.y - source_posistion2.y
self.add_rect(layer="metal1",
offset=source_posistion2,
width=drc["minwidth_metal1"],
height=height)
def connect_poly(self):
"""connect connect poly between nmos and pmos"""
# connect pmos1 poly
nmos_gate = (self.nmos_position1
+ self.nmos.poly_positions[0]
+ vector(0.5 * drc["minwidth_poly"], 0))
for i in range(len(self.pmos.poly_positions)):
pmos_gate = (self.pmos_position1
+ self.pmos.poly_positions[i]
+ vector(0.5 * drc["minwidth_poly"], 0))
mid1 = [pmos_gate.x, pmos_gate.y - drc["poly_to_active"]]
self.add_path("poly", [nmos_gate, mid1, pmos_gate])
# connect pmos2 poly
nmos_gate = vector(self.nmos_position2[0]
+ self.nmos.poly_positions[0].x
+ 0.5 * drc["minwidth_poly"],
self.nmos_position1.y
+ self.nmos.poly_positions[0].y)
for i in range(len(self.pmos.poly_positions)):
pmos_gate = (self.pmos_position2
+ self.pmos.poly_positions[i]
+ vector(0.5 * drc["minwidth_poly"], 0))
mid1 = vector(pmos_gate.x,
nmos_gate.y + self.nmos.height
+ drc["poly_to_active"])
self.add_path("poly", [nmos_gate, mid1, pmos_gate])
def route_pins(self):
self.route_input_gate()
self.route_output()
def route_input_gate(self):
self.route_input_A()
self.route_input_B()
def route_input_A(self):
"""create input A layout"""
xoffset = self.nmos.poly_positions[0].x
# HACK: added 1.5, since we're going to rewrite this.
yoffset = self.nmos_position1.y + drc["well_enclosure_active"] + self.nmos.active_height + 1.5*self.poly_contact.height
self.A_position = vector(xoffset, yoffset)
# gate input
offset = self.A_position - vector(0, 0.5 * self.poly_contact.width)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
# connect gate input to tx gate
offset = self.A_position - vector(self.poly_contact.first_layer_position.y,
0.5 * self.poly_contact.width)
self.add_rect(layer="poly",
offset=offset,
width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
height=self.poly_contact.first_layer_width)
# extend the metal to the boundary of the cell
input_length = self.A_position.x
offset = [0, self.A_position.y - 0.5 * drc["minwidth_metal1"]]
self.add_layout_pin(text="A",
layer="metal1",
offset=offset,
width=input_length,
height=drc["minwidth_metal1"])
def route_input_B(self):
"""create input B layout """
xoffset = self.pmos.poly_positions[0].x \
+ self.pmos_position2.x
yoffset = self.A_position.y \
+ max(drc["minwidth_metal2"], self.poly_contact.second_layer_width) + drc["metal2_to_metal2"]
self.B_position = vector(xoffset, yoffset)
offset = self.B_position - vector(0, 0.5 * self.poly_contact.width)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
self.add_rect(layer="poly",
offset=offset,
width=-(self.poly_contact.first_layer_position.y + drc["minwidth_poly"]),
height=self.poly_contact.first_layer_width)
self.add_layout_pin(text="B",
layer="metal1",
offset=[0,
self.B_position.y - 0.5 * drc["minwidth_metal1"]],
width=self.B_position.x,
height=drc["minwidth_metal1"])
def route_output(self):
"""route the output to nmos pmos """
self.Z_position = vector(self.width, self.A_position.y)
# route nmos drain to Z
nmos_contact = (self.nmos_position1
+ self.nmos.active_contact_positions[1]
+ self.nmos.active_contact.second_layer_position
+ vector(self.nmos.active_contact.second_layer_width,
0).scale(0.5, 0))
mid = [nmos_contact.x, self.A_position.y]
self.add_path("metal1", [self.Z_position, mid, nmos_contact])
for i in range(len(self.pmos.poly_positions) + 1):
if i % 2 == 1:
# pmos2 drain to Z
pmos_contact = (self.pmos_position2
+ self.pmos.active_contact_positions[i]
+ self.pmos.active_contact.second_layer_position.scale(1, 0)
+ vector(self.pmos.active_contact.second_layer_width,
0).scale(0.5, 0))
offset = pmos_contact - vector(0.5 * self.m1m2_via.width, 0)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
mid = [pmos_contact.x, self.Z_position.y]
self.add_wire(("metal1", "via1", "metal2"),
[self.Z_position, mid, pmos_contact])
self.add_layout_pin(text="Z",
layer="metal1",
offset=mid - vector(0,0.5*drc["minwidth_metal1"]),
width=self.Z_position.x-mid[0],
height=drc["minwidth_metal1"])
def extend_wells(self):
""" extend well for well contact"""
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 )
self.nwell_position = vector(0, middle_point)
self.nwell_height = self.height - middle_point
self.add_rect(layer="nwell",
offset=self.nwell_position,
width=self.well_width,
height=self.nwell_height)
self.add_rect(layer="vtg",
offset=self.nwell_position,
width=self.well_width,
height=self.nwell_height)
self.pwell_position = vector(0, 0)
self.pwell_height = middle_point
self.add_rect(layer="pwell",
offset=self.pwell_position,
width=self.well_width,
height=self.pwell_height)
self.add_rect(layer="vtg",
offset=self.pwell_position,
width=self.well_width,
height=self.pwell_height)
def extend_active(self):
""" extend active for well contact"""
self.active_width = self.pmos.active_width \
+ drc["active_to_body_active"] \
+ self.pmos.active_contact.width
offset = (self.pmos_position2.scale(1,0)
+ self.pmos_position1.scale(0,1)
+ self.pmos.active_position)
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.active_position.scale(1,-1)
+ self.nmos_position2.scale(1,0)
+ self.nmos_position1.scale(0,1)
+ vector(0, self.nmos.height - 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)

189
compiler/pgate.py Normal file
View File

@ -0,0 +1,189 @@
import contact
import design
import debug
from tech import drc, parameter, spice, info
from ptx import ptx
from vector import vector
from globals import OPTS
class pgate(design.design):
"""
This is a module that implements some shared functions for parameterized gates.
"""
def __init__(self, name):
""" Creates a generic cell """
design.design.__init__(self, name)
def connect_pin_to_rail(self,inst,pin,supply):
""" Conencts a ptx pin to a supply rail. """
source_pin = inst.get_pin(pin)
supply_pin = self.get_pin(supply)
if supply_pin.overlaps(source_pin):
return
if supply=="gnd":
height=supply_pin.by()-source_pin.by()
elif supply=="vdd":
height=supply_pin.uy()-source_pin.by()
else:
debug.error("Invalid supply name.",-1)
if abs(height)>0:
self.add_rect(layer="metal1",
offset=source_pin.ll(),
height=height,
width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=90):
""" Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or right of gate. """
nmos_gate_pin = nmos_inst.get_pin("G")
pmos_gate_pin = pmos_inst.get_pin("G")
# Check if the gates are aligned and give an error if they aren't!
debug.check(nmos_gate_pin.ll().x==pmos_gate_pin.ll().x, "Connecting unaligned gates not supported.")
# Pick point on the left of NMOS and connect down to PMOS
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5*drc["minwidth_poly"],0)
pmos_gate_pos = vector(nmos_gate_pos.x,pmos_gate_pin.bc().y)
self.add_path("poly",[nmos_gate_pos,pmos_gate_pos])
# Add the via to the cell midpoint along the gate
left_gate_offset = vector(nmos_gate_pin.lx(),ypos)
# Center is completely symmetric.
if rotate==90:
contact_width = contact.poly.height
contact_m1_width = contact.poly.second_layer_height
contact_m1_height = contact.poly.second_layer_width
else:
contact_width = contact.poly.width
contact_m1_width = contact.poly.second_layer_width
contact_m1_height = contact.poly.second_layer_height
if position=="center":
contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0)
elif position=="left":
contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0)
elif position=="right":
contact_offset = left_gate_offset + vector(0.5*contact.width + 0.5*self.poly_width, 0)
else:
debug.error("Invalid contact placement option.", -1)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=contact_offset,
rotate=rotate)
# self.add_layout_pin_center_segment(text=name,
# layer="metal1",
# start=left_gate_offset.scale(0,1),
# end=left_gate_offset)
self.add_layout_pin_center_rect(text=name,
layer="metal1",
offset=contact_offset,
width=contact_m1_width,
height=contact_m1_height)
# This is to ensure that the contact is connected to the gate
mid_point = contact_offset.scale(0.5,1)+left_gate_offset.scale(0.5,0)
self.add_rect_center(layer="poly",
offset=mid_point,
height=contact.poly.first_layer_width,
width=left_gate_offset.x-contact_offset.x)
def extend_wells(self, middle_position):
""" Extend the n/p wells to cover whole cell """
nwell_height = self.height - middle_position.y
if info["has_nwell"]:
self.add_rect(layer="nwell",
offset=middle_position,
width=self.well_width,
height=nwell_height)
self.add_rect(layer="vtg",
offset=middle_position,
width=self.well_width,
height=nwell_height)
if info["has_pwell"]:
self.add_rect(layer="pwell",
offset=vector(0,0),
width=self.well_width,
height=middle_position.y)
self.add_rect(layer="vtg",
offset=vector(0,0),
width=self.well_width,
height=middle_position.y)
def add_nwell_contact(self, nmos, nmos_pos):
""" Add an nwell contact next to the given nmos device. """
layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the nmos right active edge
nwell_contact_xoffset = nmos_pos.x + nmos.active_width + drc["active_to_body_active"]
nwell_contact_yoffset = nmos_pos.y
nwell_offset = vector(nwell_contact_xoffset, nwell_contact_yoffset)
# Offset by half a contact in x and y
nwell_offset += vector(0.5*nmos.active_contact.first_layer_width,
0.5*nmos.active_contact.first_layer_height)
self.nwell_contact=self.add_contact_center(layers=layer_stack,
offset=nwell_offset)
self.add_rect_center(layer="metal1",
offset=nwell_offset.scale(1,0.5),
width=self.nwell_contact.second_layer_width,
height=nwell_offset.y)
# Now add the full active and implant for the NMOS
nwell_offset = nmos_pos + vector(nmos.active_width,0)
nwell_contact_width = drc["active_to_body_active"] + nmos.active_contact.width
self.add_rect(layer="active",
offset=nwell_offset,
width=nwell_contact_width,
height=nmos.active_height)
implant_offset = nwell_offset + vector(drc["implant_to_implant"],0)
implant_width = nwell_contact_width - drc["implant_to_implant"]
self.add_rect(layer="pimplant",
offset=implant_offset,
width=implant_width,
height=nmos.active_height)
def add_pwell_contact(self, pmos, pmos_pos):
""" Add an pwell contact next to the given pmos device. """
layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the pmos right active edge
pwell_contact_xoffset = pmos_pos.x + pmos.active_width + drc["active_to_body_active"]
pwell_contact_yoffset = pmos_pos.y + pmos.active_height - pmos.active_contact.height
pwell_offset = vector(pwell_contact_xoffset, pwell_contact_yoffset)
# Offset by half a contact
pwell_offset += vector(0.5*pmos.active_contact.first_layer_width,
0.5*pmos.active_contact.first_layer_height)
self.pwell_contact=self.add_contact_center(layers=layer_stack,
offset=pwell_offset)
self.add_rect_center(layer="metal1",
offset=pwell_offset + vector(0,0.5*(self.height-pwell_offset.y)),
width=self.pwell_contact.second_layer_width,
height=self.height - pwell_offset.y)
# Now add the full active and implant for the PMOS
pwell_offset = pmos_pos + vector(pmos.active_width,0)
pwell_contact_width = drc["active_to_body_active"] + pmos.active_contact.width
self.add_rect(layer="active",
offset=pwell_offset,
width=pwell_contact_width,
height=pmos.active_height)
implant_offset = pwell_offset + vector(drc["implant_to_implant"],0)
implant_width = pwell_contact_width - drc["implant_to_implant"]
self.add_rect(layer="nimplant",
offset=implant_offset,
width=implant_width,
height=pmos.active_height)

View File

@ -39,6 +39,30 @@ class pin_layout:
else:
return False
def overlaps(self, other):
""" Check if a shape overlaps with a rectangle """
ll = self.rect[0]
ur = self.rect[1]
oll = other.rect[0]
our = other.rect[1]
# Start assuming no overlaps
x_overlaps = False
y_overlaps = False
# check if self is within other x range
if (ll.x >= oll.x and ll.x <= our.x) or (ur.x >= oll.x and ur.x <= our.x):
x_overlaps = True
# check if other is within self x range
if (oll.x >= ll.x and oll.x <= ur.x) or (our.x >= ll.x and our.x <= ur.x):
x_overlaps = True
# check if self is within other y range
if (ll.y >= oll.y and ll.y <= our.y) or (ur.y >= oll.y and ur.y <= our.y):
y_overlaps = True
# check if other is within self y range
if (oll.y >= ll.y and oll.y <= ur.y) or (our.y >= ll.y and our.y <= ur.y):
y_overlaps = True
return x_overlaps and y_overlaps
def height(self):
""" Return height. Abs is for pre-normalized value."""
return abs(self.rect[1].y-self.rect[0].y)

View File

@ -1,5 +1,5 @@
import contact
import design
import pgate
import debug
from tech import drc, parameter, spice, info
from ptx import ptx
@ -8,7 +8,7 @@ from math import ceil
from globals import OPTS
from utils import round_to_grid
class pinv(design.design):
class pinv(pgate.pgate):
"""
Pinv generates gds of a parametrically sized inverter. The
size is specified as the drive size (relative to minimum NMOS) and
@ -27,16 +27,16 @@ class pinv(design.design):
# 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 = "pinv{0}".format(pinv.unique_id)
name = "pinv_{}".format(pinv.unique_id)
pinv.unique_id += 1
design.design.__init__(self, name)
pgate.pgate.__init__(self, name)
debug.info(2, "create pinv structure {0} with size of {1}".format(name, size))
self.nmos_size = size
self.pmos_size = beta*size
self.beta = beta
self.height = height # Maybe minimize height if not defined in future?
self.route_output = route_output
self.route_output = False
self.add_pins()
self.create_layout()
@ -52,20 +52,16 @@ class pinv(design.design):
def create_layout(self):
""" Calls all functions related to the generation of the layout """
# 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.determine_tx_mults()
self.create_ptx()
self.setup_layout_constants()
self.add_supply_rails()
self.add_ptx()
self.extend_wells()
self.extend_wells(self.well_pos)
self.add_well_contacts()
self.connect_rails()
self.route_input_gate()
self.route_output_drain()
self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", rotate=0)
self.route_outputs()
def determine_tx_mults(self):
@ -73,8 +69,6 @@ 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)
@ -83,16 +77,18 @@ class pinv(design.design):
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"])
min_channel = max(contact.poly.width + self.m1_space,
contact.poly.width + 2*drc["poly_to_active"])
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-nmos.get_pin("D").by(),0)
# 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
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc["poly_extend_active"], self.poly_space)
total_height = tx_height + min_channel + 2*self.top_bottom_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 - 2*self.top_bottom_cell_space
tx_height_available = self.height - min_channel - 2*self.top_bottom_space
# Divide the height in half. Could divide proportional to beta, but this makes
# connecting wells of multiple cells easier.
# Subtract the poly space under the rail of the tx
@ -132,8 +128,9 @@ class pinv(design.design):
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.middle_position = vector(0,0.5*self.height)
# This will help with the wells
self.well_pos = vector(0,0.4*self.height)
def create_ptx(self):
@ -172,75 +169,26 @@ class pinv(design.design):
# place PMOS so it is half a poly spacing down from the top
self.pmos_pos = self.pmos.active_offset.scale(1,0) \
+ vector(0, self.height-self.pmos.active_height-self.top_bottom_cell_space)
+ vector(0, self.height-self.pmos.active_height-self.top_bottom_space)
self.pmos_inst=self.add_inst(name="pinv_pmos",
mod=self.pmos,
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
self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.top_bottom_cell_space)
self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.top_bottom_space)
self.nmos_inst=self.add_inst(name="pinv_nmos",
mod=self.nmos,
offset=self.nmos_pos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
def extend_wells(self):
""" Extend the n/p wells to cover whole cell """
nwell_height = self.height - self.middle_position.y
if info["has_nwell"]:
self.add_rect(layer="nwell",
offset=self.middle_position,
width=self.well_width,
height=nwell_height)
self.add_rect(layer="vtg",
offset=self.middle_position,
width=self.well_width,
height=nwell_height)
if info["has_pwell"]:
self.add_rect(layer="pwell",
offset=vector(0,0),
width=self.well_width,
height=self.middle_position.y)
self.add_rect(layer="vtg",
offset=vector(0,0),
width=self.well_width,
height=self.middle_position.y)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,0.5*(self.pmos_pos.y+self.nmos_pos.y+self.nmos.active_height))
def route_input_gate(self):
""" Route the input gate to the left side of the cell for access """
nmos_gate_pin = self.nmos_inst.get_pin("G")
pmos_gate_pin = self.pmos_inst.get_pin("G")
# Pick point on the left of NMOS and connect down to PMOS
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5*drc["minwidth_poly"],0)
pmos_gate_pos = vector(nmos_gate_pos.x,pmos_gate_pin.bc().y)
self.add_path("poly",[nmos_gate_pos,pmos_gate_pos])
# Add the via to the cell midpoint along the gate
left_gate_offset = vector(nmos_gate_pin.lx(),self.middle_position.y)
contact_offset = left_gate_offset - vector(0.5*self.poly_contact.height,0)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=contact_offset,
rotate=90)
self.add_layout_pin_center_segment(text="A",
layer="metal1",
start=left_gate_offset.scale(0,1),
end=left_gate_offset)
# This is to ensure that the contact is connected to the gate
mid_point = contact_offset.scale(0.5,1)+left_gate_offset.scale(0.5,0)
self.add_rect_center(layer="poly",
offset=mid_point,
height=self.poly_contact.first_layer_width,
width=left_gate_offset.x-contact_offset.x)
def route_output_drain(self):
def route_outputs(self):
""" Route the output (drains) together. Optionally, routes output to edge. """
# Get the drain pins
@ -253,9 +201,8 @@ class pinv(design.design):
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos])
# Remember the mid for the output
mid_drain_offset = vector(nmos_drain_pos.x,self.middle_position.y)
output_length = self.width - mid_drain_offset.x
mid_drain_offset = vector(nmos_drain_pos.x,self.output_pos.y)
if self.route_output == True:
# This extends the output to the edge of the cell
output_offset = mid_drain_offset.scale(0,1) + vector(self.width,0)
@ -265,85 +212,25 @@ class pinv(design.design):
end=output_offset)
else:
# This leaves the output as an internal pin (min sized)
self.add_layout_pin(text="Z",
layer="metal1",
offset=mid_drain_offset)
self.add_layout_pin_center_rect(text="Z",
layer="metal1",
offset=mid_drain_offset + vector(0.5*self.m1_width,0))
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the nmos right active edge
nwell_contact_xoffset = self.nmos_pos.x + self.nmos.active_width + drc["active_to_body_active"]
nwell_contact_yoffset = self.nmos_pos.y
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)
self.add_path("metal1",[nwell_offset,nwell_offset.scale(1,0)])
# Now add the full active and implant for the PMOS
nwell_offset = self.nmos_pos + vector(self.nmos.active_width,0)
nwell_contact_width = drc["active_to_body_active"] + self.nmos.active_contact.width
self.add_rect(layer="active",
offset=nwell_offset,
width=nwell_contact_width,
height=self.nmos.active_height)
self.add_rect(layer="pimplant",
offset=nwell_offset,
width=nwell_contact_width,
height=self.nmos.active_height)
# To the right a spacing away from the pmos right active edge
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)
self.add_path("metal1",[pwell_offset,vector(pwell_offset.x,self.height)])
# Now add the full active and implant for the PMOS
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,
width=pwell_contact_width,
height=self.pmos.active_height)
self.add_rect(layer="nimplant",
offset=pwell_offset,
width=pwell_contact_width,
height=self.pmos.active_height)
self.add_nwell_contact(self.nmos, self.nmos_pos)
self.add_pwell_contact(self.pmos, self.pmos_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
nmos_source_pin = self.nmos_inst.get_pin("S")
gnd_pin = self.get_pin("gnd")
# Only if they don't overlap already
if gnd_pin.uy() < nmos_source_pin.by():
self.add_rect(layer="metal1",
offset=nmos_source_pin.ll(),
height=-1*nmos_source_pin.by(),
width=nmos_source_pin.width())
self.connect_pin_to_rail(self.nmos_inst,"S","gnd")
pmos_source_pin = self.pmos_inst.get_pin("S")
vdd_pin = self.get_pin("vdd")
# Only if they don't overlap already
if vdd_pin.by() > pmos_source_pin.uy():
self.add_rect(layer="metal1",
offset=pmos_source_pin.ll(),
height=vdd_pin.by()-pmos_source_pin.by(),
width=pmos_source_pin.width())
self.connect_pin_to_rail(self.pmos_inst,"S","vdd")
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]

215
compiler/pnand2.py Normal file
View File

@ -0,0 +1,215 @@
import contact
import pgate
import debug
from tech import drc, parameter, spice
from ptx import ptx
from vector import vector
from globals import OPTS
class pnand2(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
This model use ptx to generate a 2-input nand within a cetrain height.
"""
c = reload(__import__(OPTS.config.bitcell))
bitcell = getattr(c, OPTS.config.bitcell)
unique_id = 1
def __init__(self, size=1, height=bitcell.height):
""" Creates a cell for a simple 2 input nand """
name = "pnand2_{0}".format(pnand2.unique_id)
pnand2.unique_id += 1
pgate.pgate.__init__(self, name)
debug.info(2, "create pnand2 structure {0} with size of {1}".format(name, size))
self.nmos_size = 2*size
self.pmos_size = parameter["beta"]*size
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
self.pmos_width = self.pmos_size*drc["minwidth_tx"]
self.height = height
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnand2 is only supported now.")
self.tx_mults = 1
self.add_pins()
self.create_layout()
#self.DRC_LVS()
def add_pins(self):
""" Adds pins for spice netlist """
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
def create_layout(self):
""" Calls all functions related to the generation of the layout """
self.create_ptx()
self.setup_layout_constants()
self.add_supply_rails()
self.add_ptx()
self.add_well_contacts()
self.connect_rails()
self.extend_wells(self.well_pos)
self.route_inputs()
self.route_output()
def create_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = ptx(width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly.first_layer_width,
self.m1_space + contact.m1m2.first_layer_width,
self.m2_space + contact.m2m3.first_layer_width,
self.m3_space + contact.m2m3.second_layer_width)
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + contact.active.width \
+ 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"]
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This will help with the wells
self.well_pos = vector(0,0.4*self.height)
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(),0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc["poly_extend_active"], self.poly_space)
def add_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_center_rect(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
width=self.width)
self.add_layout_pin_center_rect(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
width=self.width)
def add_ptx(self):
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.pmos1_inst=self.add_inst(name="pnand2_pmos1",
mod=self.pmos,
offset=pmos1_pos)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
mod=self.pmos,
offset=self.pmos2_pos)
self.connect_inst(["Z", "B", "vdd", "vdd"])
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
self.nmos1_inst=self.add_inst(name="pnand2_nmos1",
mod=self.nmos,
offset=nmos1_pos)
self.connect_inst(["Z", "A", "net1", "gnd"])
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst=self.add_inst(name="pnand2_nmos2",
mod=self.nmos,
offset=self.nmos2_pos)
self.connect_inst(["net1", "B", "gnd", "gnd"])
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.nmos, self.nmos2_pos)
self.add_pwell_contact(self.pmos, self.pmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.pmos2_inst,"D","vdd")
def route_inputs(self):
""" Route the A and B inputs """
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + 0.5*self.m2_width
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D")
# NMOS2 drain
nmos_pin = self.nmos2_inst.get_pin("D")
# Output pin
mid_offset = vector(nmos_pin.center().x,self.inputA_yoffset)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=mid_offset,
rotate=90)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid_offset, nmos_pin.uc()])
# This extends the output to the edge of the cell
self.add_layout_pin_center_rect(text="Z",
layer="metal1",
offset=mid_offset,
width=contact.m1m2.first_layer_height,
height=contact.m1m2.first_layer_width)
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_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)

231
compiler/pnand3.py Normal file
View File

@ -0,0 +1,231 @@
import contact
import pgate
import debug
from tech import drc, parameter, spice
from ptx import ptx
from vector import vector
from globals import OPTS
class pnand3(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
This model use ptx to generate a 2-input nand within a cetrain height.
"""
c = reload(__import__(OPTS.config.bitcell))
bitcell = getattr(c, OPTS.config.bitcell)
unique_id = 1
def __init__(self, size=1, height=bitcell.height):
""" Creates a cell for a simple 3 input nand """
name = "pnand3_{0}".format(pnand3.unique_id)
pnand3.unique_id += 1
pgate.pgate.__init__(self, name)
debug.info(2, "create pnand3 structure {0} with size of {1}".format(name, size))
self.nmos_size = 3*size
self.pmos_size = parameter["beta"]*size
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
self.pmos_width = self.pmos_size*drc["minwidth_tx"]
self.height = height
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnand3 is only supported now.")
self.tx_mults = 1
self.add_pins()
self.create_layout()
#self.DRC_LVS()
def add_pins(self):
""" Adds pins for spice netlist """
self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"])
def create_layout(self):
""" Calls all functions related to the generation of the layout """
self.create_ptx()
self.setup_layout_constants()
self.add_supply_rails()
self.add_ptx()
self.add_well_contacts()
self.connect_rails()
self.extend_wells(self.well_pos)
self.route_inputs()
self.route_output()
def create_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = ptx(width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# Compute the overlap of the source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# 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*drc["active_to_body_active"] + 2*drc["well_enclosure_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)
self.well_pos = vector(0,0.4*self.height)
# This is a poly-to-poly of a flipped cell
# This is extra liberal for pnand3 because we know there are big transistor sizes
# and so contacts won't interfere with the rails. Therefore, ignore metal spacing.
# We need to do this to fit the 3 inputs in with M2M3 via accessibility.
self.top_bottom_space = max(drc["poly_extend_active"], self.poly_space)
def add_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_center_rect(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
width=self.width)
self.add_layout_pin_center_rect(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
width=self.width)
def add_ptx(self):
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.pmos1_inst=self.add_inst(name="pnand3_pmos1",
mod=self.pmos,
offset=pmos1_pos)
self.connect_inst(["vdd", "A", "Z", "vdd"])
pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
mod=self.pmos,
offset=pmos2_pos)
self.connect_inst(["Z", "B", "vdd", "vdd"])
self.pmos3_pos = pmos2_pos + self.overlap_offset
self.pmos3_inst = self.add_inst(name="pnand3_pmos3",
mod=self.pmos,
offset=self.pmos3_pos)
self.connect_inst(["Z", "C", "vdd", "vdd"])
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
self.nmos1_inst=self.add_inst(name="pnand3_nmos1",
mod=self.nmos,
offset=nmos1_pos)
self.connect_inst(["Z", "A", "net1", "gnd"])
nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst=self.add_inst(name="pnand3_nmos2",
mod=self.nmos,
offset=nmos2_pos)
self.connect_inst(["net1", "B", "net2", "gnd"])
self.nmos3_pos = nmos2_pos + self.overlap_offset
self.nmos3_inst=self.add_inst(name="pnand3_nmos3",
mod=self.nmos,
offset=self.nmos3_pos)
self.connect_inst(["net2", "C", "gnd", "gnd"])
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.nmos, self.nmos3_pos)
self.add_pwell_contact(self.pmos, self.pmos3_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.pmos2_inst,"D","vdd")
def route_inputs(self):
""" Route the A and B inputs """
# wire space or wire and one contact space
metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width,
self.m1_space + 0.5*contact.poly.width + 0.5*self.m1_width)
active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc["poly_to_active"])
inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing
self.route_input_gate(self.pmos3_inst, self.nmos3_inst, inputC_yoffset, "C", position="center")
inputB_yoffset = inputC_yoffset + metal_spacing
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
self.inputA_yoffset = inputB_yoffset + metal_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain
pmos3_pin = self.pmos3_inst.get_pin("D")
# NMOS3 drain
nmos3_pin = self.nmos3_inst.get_pin("D")
# Go up to metal2 for ease on all output pins
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=pmos1_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=pmos3_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=nmos3_pin.center())
# PMOS3 and NMOS3 are drain aligned
self.add_path("metal2",[pmos3_pin.bc(), nmos3_pin.uc()])
# Route in the A input track (top track)
mid_offset = vector(nmos3_pin.center().x,self.inputA_yoffset)
self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=mid_offset)
self.add_layout_pin_center_rect(text="Z",
layer="metal1",
offset=mid_offset,
width=contact.m1m2.first_layer_width,
height=contact.m1m2.first_layer_height)
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_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)

225
compiler/pnor2.py Normal file
View File

@ -0,0 +1,225 @@
import contact
import pgate
import debug
from tech import drc, parameter, spice
from ptx import ptx
from vector import vector
from globals import OPTS
class pnor2(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nor.
This model use ptx to generate a 2-input nor within a cetrain height.
"""
c = reload(__import__(OPTS.config.bitcell))
bitcell = getattr(c, OPTS.config.bitcell)
unique_id = 1
def __init__(self, size=1, height=bitcell.height):
""" Creates a cell for a simple 2 input nor """
name = "pnor2_{0}".format(pnor2.unique_id)
pnor2.unique_id += 1
pgate.pgate.__init__(self, name)
debug.info(2, "create pnor2 structure {0} with size of {1}".format(name, size))
self.nmos_size = size
# We will just make this 1.5 times for now. NORs are not ideal anyhow.
self.pmos_size = 1.5*parameter["beta"]*size
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
self.pmos_width = self.pmos_size*drc["minwidth_tx"]
self.height = height
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnor2 is only supported now.")
self.tx_mults = 1
self.add_pins()
self.create_layout()
#self.DRC_LVS()
def add_pins(self):
""" Adds pins for spice netlist """
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
def create_layout(self):
""" Calls all functions related to the generation of the layout """
self.create_ptx()
self.setup_layout_constants()
self.add_supply_rails()
self.add_ptx()
self.add_well_contacts()
self.connect_rails()
self.extend_wells(self.well_pos)
self.route_inputs()
self.route_output()
def create_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = ptx(width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
poly_contact = contact.contact(("poly","contact","metal1"))
m1m2_via = contact.contact(("metal1","via1","metal2"))
m2m3_via = contact.contact(("metal2","via2","metal3"))
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + poly_contact.first_layer_width,
self.m1_space + m1m2_via.first_layer_width,
self.m2_space + m2m3_via.first_layer_width,
self.m3_space + m2m3_via.second_layer_width)
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \
+ 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"]
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This will help with the wells
self.well_pos = vector(0,0.4*self.height)
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(),0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc["poly_extend_active"], self.poly_space)
def add_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_center_rect(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
width=self.width)
self.add_layout_pin_center_rect(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
width=self.width)
def add_ptx(self):
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.pmos1_inst=self.add_inst(name="pnor2_pmos1",
mod=self.pmos,
offset=pmos1_pos)
self.connect_inst(["vdd", "A", "net1", "vdd"])
self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
mod=self.pmos,
offset=self.pmos2_pos)
self.connect_inst(["net1", "B", "Z", "vdd"])
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
self.nmos1_inst=self.add_inst(name="pnor2_nmos1",
mod=self.nmos,
offset=nmos1_pos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst=self.add_inst(name="pnor2_nmos2",
mod=self.nmos,
offset=self.nmos2_pos)
self.connect_inst(["Z", "B", "gnd", "gnd"])
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.nmos, self.nmos2_pos)
self.add_pwell_contact(self.pmos, self.pmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
self.connect_pin_to_rail(self.nmos2_inst,"D","gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
def route_inputs(self):
""" Route the A and B inputs """
# Use M2 spaces so we can drop vias on the pins later!
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + self.m2_width
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A")
def route_output(self):
""" Route the Z output """
# PMOS2 drain
pmos_pin = self.pmos2_inst.get_pin("D")
# NMOS1 drain
nmos_pin = self.nmos1_inst.get_pin("D")
# NMOS2 drain (for output via placement)
nmos2_pin = self.nmos2_inst.get_pin("D")
# Go up to metal2 for ease on all output pins
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center())
m1m2_contact=self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center(),
rotate=90)
mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width,0)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset])
# This extends the output to the edge of the cell
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=mid3_offset,
rotate=90)
self.add_layout_pin_center_rect(text="Z",
layer="metal1",
offset=mid3_offset,
width=contact.m1m2.first_layer_height,
height=contact.m1m2.first_layer_width)
def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_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)

View File

@ -1,27 +1,27 @@
from contact import contact
import design
import contact
import pgate
import debug
from tech import drc
from tech import drc, parameter
from ptx import ptx
from vector import vector
from globals import OPTS
class precharge(design.design):
class precharge(pgate.pgate):
"""
Creates a single precharge cell
This module implements the precharge bitline cell used in the design.
"""
def __init__(self, name, ptx_width, beta=2):
design.design.__init__(self, name)
def __init__(self, name, size=1):
pgate.pgate.__init__(self, name)
debug.info(2, "create single precharge cell: {0}".format(name))
c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
self.bitcell = self.mod_bitcell()
self.ptx_width = ptx_width
self.beta = beta
self.beta = parameter["beta"]
self.ptx_width = self.beta*parameter["min_tx_size"]
self.width = self.bitcell.width
self.add_pins()
@ -33,174 +33,123 @@ class precharge(design.design):
def create_layout(self):
self.create_ptx()
self.create_contacts()
self.add_ptx()
self.connect_poly()
self.add_pclk()
self.add_nwell_contact()
self.extend_nwell()
self.add_nwell_and_contact()
self.add_vdd_rail()
self.add_bitlines()
self.connect_to_bitlines()
def create_ptx(self):
"""Initializes the upper and lower pmos"""
self.lower_pmos = ptx(width=self.ptx_width,
tx_type="pmos")
self.add_mod(self.lower_pmos)
self.upper_pmos = ptx(width=self.beta * self.ptx_width,
tx_type="pmos")
self.upper_pmos = self.upper_pmos
self.add_mod(self.upper_pmos)
self.temp_pmos = ptx(width=self.beta * self.ptx_width,
mults=2,
tx_type="pmos")
#self.temp_pmos.remove_source_connect()
#self.temp_pmos.remove_drain_connect()
self.pmos = ptx(width=self.ptx_width,
tx_type="pmos")
self.add_mod(self.pmos)
def create_contacts(self):
"""Initializes all required contacts/vias for this module"""
# These aren't for instantiating, but we use them to get the dimensions
self.nwell_contact = contact(layer_stack=("active", "contact", "metal1"))
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
self.upper_dimensions = self.upper_pmos.active_contact.dimensions
self.lower_dimensions = self.lower_pmos.active_contact.dimensions
self.upper_contact = contact(layer_stack=("metal1", "via1", "metal2"),
dimensions=self.upper_dimensions)
self.lower_contact = contact(layer_stack=("metal1", "via1", "metal2"),
dimensions=self.lower_dimensions)
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
def add_ptx(self):
"""Adds both the upper_pmos and lower_pmos to the module"""
# adds the lower pmos to layout
base = vector(self.width - self.temp_pmos.width, 0).scale(0.5,0)
self.lower_pmos_position = base + vector([drc["metal1_to_metal1"]]*2)
self.add_inst(name="lower_pmos",
mod=self.lower_pmos,
offset=self.lower_pmos_position)
self.connect_inst(["bl", "clk", "br", "vdd"])
# adds the upper pmos(s) to layout
ydiff = (self.lower_pmos.height
+ 2 * drc["metal1_to_metal1"]
+ self.poly_contact.width)
self.upper_pmos_position = self.lower_pmos_position + vector(0, ydiff)
self.add_inst(name="upper_pmos1",
mod=self.upper_pmos,
offset=self.upper_pmos_position)
self.connect_inst(["bl", "clk", "vdd", "vdd"])
xdiff =(self.upper_pmos.active_contact_positions[-1].x
- self.upper_pmos.active_contact_positions[0].x)
self.upper_pmos_position2 = self.upper_pmos_position + vector(xdiff, 0)
self.add_inst(name="upper_pmos2",
mod=self.upper_pmos,
offset=self.upper_pmos_position2)
self.connect_inst(["br", "clk", "vdd", "vdd"])
def connect_poly(self):
"""Connects the upper and lower pmos together"""
offset = (self.lower_pmos_position
+ self.lower_pmos.poly_positions[0]
+ vector(0,self.lower_pmos.poly_height))
# connects the top and bottom pmos' gates together
ylength = (self.upper_pmos_position.y
+ self.upper_pmos.poly_positions[0].y
- offset.y)
self.add_rect(layer="poly",
offset=offset,
width=drc["minwidth_poly"],
height=ylength)
# connects the two poly for the two upper pmos(s)
offset = offset + vector(0, ylength - drc["minwidth_poly"])
xlength = (self.temp_pmos.poly_positions[-1].x
- self.temp_pmos.poly_positions[0].x
+ drc["minwidth_poly"])
self.add_rect(layer="poly",
offset=offset,
width=xlength,
height=drc["minwidth_poly"])
def add_pclk(self):
"""Adds the pclk input rail, pclk contact/vias, and connects to the pmos"""
# adds the pclk contact to connect the gates to the pclk rail on metal1
offset = vector(self.lower_pmos_position.x + self.lower_pmos.poly_positions[0].x
+ 0.5 * self.poly_contact.height,
self.upper_pmos_position.y - drc["metal1_to_metal1"] \
- self.poly_contact.width)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
# adds the pclk rail on metal1
offset.y= offset.y + self.poly_contact.second_layer_position.x
self.pclk_position = vector(0, offset.y)
self.add_layout_pin(text="clk",
layer="metal1",
offset=self.pclk_position,
width=self.width,
height=drc["minwidth_metal1"])
def add_nwell_contact(self):
"""Adds a nwell tap to connect to the vdd rail"""
# adds the contact from active to metal1
offset = vector(self.temp_pmos.active_contact_positions[1].x,
self.upper_pmos.height + drc["well_extend_active"]
- self.nwell_contact.first_layer_position.y)
self.nwell_contact_position = offset + self.upper_pmos_position
self.add_contact(layers=("active", "contact", "metal1"),
offset=self.nwell_contact_position)
# adds the implant to turn the contact into a nwell tap
offset = self.nwell_contact_position + self.nwell_contact.first_layer_position
xlength = self.nwell_contact.first_layer_width
ylength = self.nwell_contact.first_layer_height
self.add_rect(layer="nimplant",
offset=offset,
width=xlength,
height=ylength)
def extend_nwell(self):
"""Extends the nwell for the whole module"""
self.nwell_position = self.pclk_position.scale(1,0)
self.height = (self.nwell_contact_position.y + self.nwell_contact.height
- self.nwell_contact.first_layer_position.y
+ drc["well_extend_active"])
self.add_rect(layer="nwell",
offset=self.nwell_position,
width=self.width,
height=self.height)
def add_vdd_rail(self):
"""Adds a vdd rail at the top of the cell"""
# adds the rail across the width of the cell
vdd_position = vector(self.pclk_position.x,
self.height - drc["minwidth_metal1"])
vdd_position = vector(0, self.height - self.m1_width)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_position,
width=self.width,
height=drc["minwidth_metal1"])
height=self.m1_width)
# connects the upper pmos(s) to the vdd rail
upper_pmos_contact = (self.upper_pmos.active_contact_positions[1]
+ self.upper_pmos_position)
ylength = self.height - upper_pmos_contact.y
offset = vector(self.nwell_contact_position.x
+ self.nwell_contact.second_layer_position.x,
self.upper_pmos.active_contact.second_layer_position.y
+ upper_pmos_contact.y)
self.add_rect(layer="metal1",
self.connect_pin_to_rail(self.upper_pmos2_inst,"S","vdd")
def add_ptx(self):
"""Adds both the upper_pmos and lower_pmos to the module"""
# adds the lower pmos to layout
#base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0)
self.lower_pmos_position = vector(self.bitcell.get_pin("BL").lx(),
self.pmos.active_offset.y)
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
mod=self.pmos,
offset=self.lower_pmos_position)
self.connect_inst(["bl", "clk", "br", "vdd"])
# adds the upper pmos(s) to layout
ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
mod=self.pmos,
offset=self.upper_pmos1_pos)
self.connect_inst(["bl", "clk", "vdd", "vdd"])
upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
mod=self.pmos,
offset=upper_pmos2_pos)
self.connect_inst(["br", "clk", "vdd", "vdd"])
def connect_poly(self):
"""Connects the upper and lower pmos together"""
offset = self.lower_pmos_inst.get_pin("G").ll()
# connects the top and bottom pmos' gates together
ylength = self.upper_pmos1_inst.get_pin("G").ll().y - offset.y
self.add_rect(layer="poly",
offset=offset,
width=drc["minwidth_metal1"],
width=self.poly_width,
height=ylength)
# connects the two poly for the two upper pmos(s)
offset = offset + vector(0, ylength - self.poly_width)
xlength = self.upper_pmos2_inst.get_pin("G").lx() - self.upper_pmos1_inst.get_pin("G").lx() + self.poly_width
self.add_rect(layer="poly",
offset=offset,
width=xlength,
height=self.poly_width)
def add_pclk(self):
"""Adds the pclk input rail, pclk contact/vias, and connects to the pmos"""
# adds the pclk contact to connect the gates to the pclk rail on metal1
offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
# adds the pclk rail on metal1
self.add_layout_pin_center_segment(text="clk",
layer="metal1",
start=offset.scale(0,1),
end=offset.scale(0,1)+vector(self.width,0))
def add_nwell_and_contact(self):
"""Adds a nwell tap to connect to the vdd rail"""
# 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_pos.y + self.pmos.height + drc["well_extend_active"])
self.add_contact_center(layers=("active", "contact", "metal1"),
offset=well_contact_pos)
# adds the implant to turn the contact into a nwell tap
self.add_rect_center(layer="nimplant",
offset=well_contact_pos,
width=contact.well.first_layer_width,
height=contact.well.first_layer_height)
self.height = well_contact_pos.y + contact.well.height
self.add_rect(layer="nwell",
offset=vector(0,0),
width=self.width,
height=self.height)
def add_bitlines(self):
"""Adds both bit-line and bit-line-bar to the module"""
# adds the BL on metal 2
offset = vector(self.bitcell.get_pin("BL").cx(),0) - vector(0.5 * drc["minwidth_metal2"],0)
offset = vector(self.bitcell.get_pin("BL").cx(),0) - vector(0.5 * self.m2_width,0)
self.add_layout_pin(text="BL",
layer="metal2",
offset=offset,
@ -208,7 +157,7 @@ class precharge(design.design):
height=self.height)
# adds the BR on metal 2
offset = vector(self.bitcell.get_pin("BR").cx(),0) - vector(0.5 * drc["minwidth_metal2"],0)
offset = vector(self.bitcell.get_pin("BR").cx(),0) - vector(0.5 * self.m2_width,0)
self.add_layout_pin(text="BR",
layer="metal2",
offset=offset,
@ -217,75 +166,39 @@ class precharge(design.design):
def connect_to_bitlines(self):
self.add_bitline_contacts()
dest =[self.upper_pmos,self.upper_pmos_position,self.upper_contact]
correct_x = self.xcorrect_upper + self.upper_contact.second_layer_position.x
self.connect_pmos_to_BL(correct_x,dest)
correct_x = correct_x + self.temp_pmos.active_contact_positions[-1].x
self.connect_pmos_to_BR(correct_x,dest)
dest =[self.lower_pmos,self.lower_pmos_position,self.lower_contact]
correct_x = self.xcorrect_lower + self.lower_contact.first_layer_position.x
self.connect_pmos_to_BL(correct_x,dest)
correct_x = correct_x + self.lower_pmos.active_contact_positions[-1].x
self.connect_pmos_to_BR(correct_x,dest)
self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("BL"))
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("BR"))
self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin("BL"))
self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("BR"))
def add_bitline_contacts(self):
"""Adds contacts/via from metal1 to metal2 for bit-lines"""
# helps centers the via over the underneath contact
self.xcorrect_upper = 0.5 * abs(self.upper_contact.width
- self.upper_pmos.active_contact.width)
self.xcorrect_lower = 0.5 * abs(self.lower_contact.width
- self.lower_pmos.active_contact.width)
# adds contacts from metal1 to metal2 over the active contacts for the
# upper pmos(s)
offset = (self.upper_pmos_position
+ self.upper_pmos.active_contact_positions[0]
+ vector(self.xcorrect_upper,0))
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
size=self.upper_dimensions)
offset.x = (self.upper_pmos_position.x
+ self.temp_pmos.active_contact_positions[-1].x
+ self.xcorrect_upper)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
size=self.upper_dimensions)
for stack in [("active","contact","metal1"),("metal1", "via1", "metal2")]:
pos = self.lower_pmos_inst.get_pin("S").center()
self.add_contact_center(layers=stack,
offset=pos)
pos = self.lower_pmos_inst.get_pin("D").center()
self.add_contact_center(layers=stack,
offset=pos)
pos = self.upper_pmos1_inst.get_pin("S").center()
self.add_contact_center(layers=stack,
offset=pos)
pos = self.upper_pmos2_inst.get_pin("D").center()
self.add_contact_center(layers=stack,
offset=pos)
# adds contacts from metal1 to metal 2 over active contacts for the
# lower pmos
offset = (self.lower_pmos_position
+ self.lower_pmos.active_contact_positions[0]
+ vector(self.xcorrect_upper,0))
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
size=self.lower_dimensions)
offset.x = (self.lower_pmos_position.x
+ self.lower_pmos.active_contact_positions[-1].x
+ self.xcorrect_lower)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
size=self.lower_dimensions)
def connect_pmos(self, pmos_pin, bit_pin):
""" Connect pmos pin to bitline pin """
def connect_pmos_to_BL(self,correct_x,dest):
"""Connects bit-lines to lower_pmos"""
mos,mos_pos,contact = dest
mos_active = (mos_pos + mos.active_contact_positions[0])
offset = vector(self.bitcell.get_pin("BL").cx() , mos_active.y)
xlength = (mos_active.x + correct_x - offset.x
+ 0.5 * drc["minwidth_metal2"])
ll_pos = vector(min(pmos_pin.lx(),bit_pin.lx()), pmos_pin.by())
ur_pos = vector(max(pmos_pin.rx(),bit_pin.rx()), pmos_pin.uy())
width = ur_pos.x-ll_pos.x
height = ur_pos.y-ll_pos.y
self.add_rect(layer="metal2",
offset=offset,
width=xlength,
height=contact.height)
def connect_pmos_to_BR(self,correct_x,dest):
"""Connects bit-lines to the upper_pmos"""
mos,mos_pos,contact = dest
offset = mos_pos + vector(correct_x,
mos.active_contact_positions[0].y)
xlength = self.bitcell.get_pin("BR").cx() - offset.x - 0.5 * drc["minwidth_metal2"]
self.add_rect(layer="metal2",
offset=offset,
width=xlength,
height=contact.height)
offset=ll_pos,
width=width,
height=height)

View File

@ -11,17 +11,13 @@ class precharge_array(design.design):
of bit line columns, height is the height of the bit-cell array.
"""
def __init__(self, columns, ptx_width, beta=2):
def __init__(self, columns, size=1):
design.design.__init__(self, "precharge_array")
debug.info(1, "Creating {0}".format(self.name))
self.columns = columns
self.ptx_width = ptx_width
self.beta = beta
self.pc_cell = precharge(name="precharge_cell",
ptx_width=self.ptx_width,
beta=self.beta)
self.pc_cell = precharge(name="precharge_cell", size=size)
self.add_mod(self.pc_cell)
self.width = self.columns * self.pc_cell.width

View File

@ -49,7 +49,7 @@ class ptx(design.design):
# but this may be uncommented for debug purposes
#self.DRC()
def create_layout(self):
"""Calls all functions related to the generation of the layout"""
self.setup_layout_constants()
@ -194,35 +194,37 @@ class ptx(design.design):
end_offset = vector(self.active_contact.second_layer_width/2,0)
# drains always go to the MIDDLE of the cell, so top of NMOS, bottom of PMOS
# so reverse the directions for NMOS
# so reverse the directions for NMOS compared to PMOS.
if self.tx_type == "pmos":
drain_dir = 1
source_dir = -1
else:
drain_dir = -1
source_dir = 1
else:
drain_dir = 1
source_dir = -1
if len(source_positions)>1:
source_offset = pin_offset.scale(source_dir,source_dir)
self.remove_layout_pin("S") # remove the individual connections
# Add each vertical segment
for a in source_positions:
self.add_path(("metal1"), [a,a+pin_offset])
self.add_path(("metal1"), [a,a+pin_offset.scale(source_dir,source_dir)])
# Add a single horizontal pin
self.add_layout_pin_center_segment(text="S",
layer="metal1",
start=source_positions[0]+pin_offset-end_offset,
end=source_positions[-1]+pin_offset+end_offset)
start=source_positions[0]+source_offset-end_offset,
end=source_positions[-1]+source_offset+end_offset)
if len(drain_positions)>1:
if len(drain_positions)>1:
drain_offset = pin_offset.scale(drain_dir,drain_dir)
self.remove_layout_pin("D") # remove the individual connections
# Add each vertical segment
for a in drain_positions:
self.add_path(("metal1"), [a,a-pin_offset])
self.add_path(("metal1"), [a,a+drain_offset])
# Add a single horizontal pin
self.add_layout_pin_center_segment(text="D",
layer="metal1",
start=drain_positions[0]-pin_offset-end_offset,
end=drain_positions[-1]-pin_offset+end_offset)
start=drain_positions[0]+drain_offset-end_offset,
end=drain_positions[-1]+drain_offset+end_offset)
def add_poly(self):
"""
@ -234,7 +236,9 @@ class ptx(design.design):
# poly_positions are the bottom center of the poly gates
poly_positions = []
# It is important that these are from left to right, so that the pins are in the right
# order for the accessors
for i in range(0, self.mults):
# Add this duplicate rectangle in case we remove the pin when joining fingers
self.add_rect_center(layer="poly",
@ -295,6 +299,8 @@ class ptx(design.design):
# The first one will always be a source
source_positions = [self.contact_offset]
drain_positions = []
# It is important that these are from left to right, so that the pins are in the right
# order for the accessors.
for i in range(self.mults):
if i%2:
# It's a source... so offset from previous drain.

View File

@ -2,7 +2,7 @@ import debug
import design
from tech import drc
from pinv import pinv
from contact import contact
import contact
from bitcell_array import bitcell_array
from nor_2 import nor_2
from ptx import ptx
@ -37,6 +37,7 @@ class replica_bitline(design.design):
self.add_modules()
self.route()
self.add_layout_pins()
self.add_lvs_correspondence_points()
self.DRC_LVS()
@ -44,25 +45,21 @@ class replica_bitline(design.design):
""" Calculate all the module offsets """
# These aren't for instantiating, but we use them to get the dimensions
self.active_contact = contact(layer_stack=("active", "contact", "poly"))
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
self.poly_contact_offset = vector(0.5*self.poly_contact.width,0.5*self.poly_contact.height)
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space,self.m2_space)
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
# This corrects the offset pitch difference between M2 and M1
self.offset_fix = vector(0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]),0)
self.offset_fix = vector(0.5*(self.m2_width-self.m1_width),0)
# delay chain will be rotated 90, so move it over a width
# we move it up a inv height just for some routing room
self.rbl_inv_offset = vector(self.delay_chain.height, self.inv.width)
# access TX goes right on top of inverter, leave space for an inverter which is
# about the same as a TX. We'll need to add rails though.
self.access_tx_offset = vector(1.5*self.inv.height,self.rbl_inv_offset.y) + vector(0,2.25*self.inv.width)
self.access_tx_offset = vector(1.25*self.inv.height,self.rbl_inv_offset.y) + vector(0,2.5*self.inv.width)
self.delay_chain_offset = self.rbl_inv_offset + vector(0,4*self.inv.width)
# Replica bitline and such are not rotated, but they must be placed far enough
@ -145,17 +142,16 @@ class replica_bitline(design.design):
# Determines the y-coordinate of where to place the gate input poly pin
# (middle in between the pmos and nmos)
# finds the lower right of the poly gate
poly_offset = self.access_tx_offset + self.access_tx.poly_positions[0].rotate_scale(-1,1)
poly_pin = self.tx_inst.get_pin("G")
poly_offset = poly_pin.rc()
# This centers the contact on the poly
contact_offset = poly_offset.scale(0,1) + self.dc_inst.get_pin("out").ll().scale(1,0) \
+ vector(-drc["poly_extend_contact"], -0.5*self.poly_contact.height + 0.5*drc["minwidth_poly"])
self.add_contact(layers=("poly", "contact", "metal1"),
offset=contact_offset)
contact_offset = poly_offset.scale(0,1) + self.dc_inst.get_pin("out").bc().scale(1,0)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=contact_offset)
self.add_rect(layer="poly",
offset=poly_offset,
offset=poly_pin.lr(),
width=contact_offset.x-poly_offset.x,
height=drc["minwidth_poly"])
height=self.poly_width)
nwell_offset = self.rbl_inv_offset + vector(-self.inv.height,self.inv.width)
self.add_rect(layer="nwell",
offset=nwell_offset,
@ -164,46 +160,41 @@ class replica_bitline(design.design):
# 2. Route delay chain output to access tx gate
delay_en_offset = self.dc_inst.get_pin("out").bc()
delay_en_end_offset = contact_offset + vector(self.poly_contact.width,self.poly_contact.height).scale(0.5,0.5)
self.add_path("metal1", [delay_en_offset,delay_en_end_offset])
self.add_path("metal1", [delay_en_offset,contact_offset])
# 3. Route the mid-point of previous route to the bitcell WL
# route bend of previous net to bitcell WL
wl_offset = self.rbc_inst.get_pin("WL").lc()
wl_mid = vector(delay_en_end_offset.x,wl_offset.y)
self.add_path("metal1", [delay_en_end_offset, wl_mid, wl_offset])
wl_mid = vector(contact_offset.x,wl_offset.y)
self.add_path("metal1", [contact_offset, wl_mid, wl_offset])
# SOURCE ROUTE
# Route the source to the vdd rail
source_offset = self.access_tx_offset + self.access_tx.active_contact_positions[1].rotate_scale(-1,1)\
+ vector(self.active_contact.width,self.active_contact.height).rotate_scale(-0.5,0.5)
inv_vdd_offset = self.rbl_inv_inst.get_pin("vdd").uc()
vdd_offset = inv_vdd_offset.scale(1,0) + source_offset.scale(0,1)
self.add_path("metal1", [source_offset, vdd_offset])
# DRAIN ROUTE
# Route the drain to the RBL inverter input
drain_offset = self.access_tx_offset + self.access_tx.active_contact_positions[0].rotate_scale(-1,1) \
+ self.poly_contact_offset.rotate_scale(-1,1)
mid1 = drain_offset.scale(1,0) + vector(0,self.rbl_inv_offset.y+self.inv.width+self.m2_pitch)
# Route the drain to the vdd rail
drain_offset = self.tx_inst.get_pin("D").lc()
inv_vdd_offset = self.rbl_inv_inst.get_pin("vdd").uc()
vdd_offset = inv_vdd_offset.scale(1,0) + drain_offset.scale(0,1)
self.add_path("metal1", [drain_offset, vdd_offset])
# SOURCE ROUTE
# Route the source to the RBL inverter input
source_offset = self.tx_inst.get_pin("S").bc()
mid1 = source_offset.scale(1,0) + vector(0,self.rbl_inv_offset.y+self.inv.width+self.m2_pitch)
inv_A_offset = self.rbl_inv_inst.get_pin("A").uc()
mid2 = vector(inv_A_offset.x, mid1.y)
self.add_path("metal1",[drain_offset, mid1, mid2, inv_A_offset])
self.add_path("metal1",[source_offset, mid1, mid2, inv_A_offset])
# Route the connection of the drain route (mid2) to the RBL bitline (left)
drain_offset = mid2
# Route the connection of the source route (mid2) to the RBL bitline (left)
source_offset = mid2
# Route the M2 to the right of the vdd rail between rbl_inv and bitcell
gnd_pin = self.rbl_inv_inst.get_pin("gnd").ll()
mid1 = vector(gnd_pin.x+self.m2_pitch,drain_offset.y)
mid1 = vector(gnd_pin.x+self.m2_pitch,source_offset.y)
# Via will go halfway down from the bitcell
bl_offset = self.rbc_inst.get_pin("BL").bc()
via_offset = bl_offset - vector(0,0.5*self.inv.width)
mid2 = vector(mid1.x,via_offset.y)
# self.add_contact(layers=("metal1", "via1", "metal2"),
# offset=via_offset - vector(0.5*drc["minwidth_metal2"],0.5*drc["minwidth_metal1"]))
self.add_wire(("metal1","via1","metal2"),[drain_offset,mid1,mid2,via_offset,bl_offset])
# offset=via_offset - vector(0.5*self.m2_width,0.5*self.m1_width))
self.add_wire(("metal1","via1","metal2"),[source_offset,mid1,mid2,via_offset,bl_offset])
#self.add_path("metal2",[via_offset,bl_offset])
def route_vdd(self):
@ -215,8 +206,8 @@ class replica_bitline(design.design):
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_start,
width=drc["minwidth_metal1"],
height=self.rbl.height+self.bitcell.height+2*self.inv.width+0.5*drc["minwidth_metal1"])
width=self.m1_width,
height=self.rbl.height+self.bitcell.height+2*self.inv.width+0.5*self.m1_width)
# Connect the vdd pins of the bitcell load directly to vdd
vdd_pins = self.rbl_inst.get_pins("vdd")
@ -225,7 +216,7 @@ class replica_bitline(design.design):
self.add_rect(layer="metal1",
offset=offset,
width=self.rbl_offset.x-vdd_start.x,
height=drc["minwidth_metal1"])
height=self.m1_width)
# Also connect the replica bitcell vdd pin to vdd
pin = self.rbc_inst.get_pin("vdd")
@ -233,14 +224,14 @@ class replica_bitline(design.design):
self.add_rect(layer="metal1",
offset=offset,
width=self.bitcell_offset.x-vdd_start.x,
height=drc["minwidth_metal1"])
height=self.m1_width)
# Add a second vdd pin. No need for full length. It is must connect at the next level.
inv_vdd_offset = self.rbl_inv_inst.get_pin("vdd").ll()
self.add_layout_pin(text="vdd",
layer="metal1",
offset=inv_vdd_offset.scale(1,0),
width=drc["minwidth_metal1"],
width=self.m1_width,
height=self.delay_chain_offset.y)
@ -255,12 +246,12 @@ class replica_bitline(design.design):
# It is the height of the entire RBL and bitcell
self.add_rect(layer="metal2",
offset=gnd_start,
width=drc["minwidth_metal2"],
width=self.m2_width,
height=self.rbl.height+self.bitcell.height+self.inv.width+self.m2_pitch)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_start.scale(1,0),
width=drc["minwidth_metal2"],
width=self.m2_width,
height=2*self.inv.width)
# Connect the WL pins directly to gnd
@ -271,17 +262,17 @@ class replica_bitline(design.design):
self.add_rect(layer="metal1",
offset=offset,
width=self.rbl_offset.x-gnd_start.x,
height=drc["minwidth_metal1"])
height=self.m1_width)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
# Add via for the delay chain
offset = self.delay_chain_offset - vector(0.5*drc["minwidth_metal1"],0) - self.offset_fix
offset = self.delay_chain_offset - vector(0.5*self.m1_width,0) - self.offset_fix
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
# Add via for the inverter
offset = self.rbl_inv_offset - vector(0.5*drc["minwidth_metal1"],self.m1m2_via.height) - self.offset_fix
offset = self.rbl_inv_offset - vector(0.5*self.m1_width,contact.m1m2.height) - self.offset_fix
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
@ -306,24 +297,44 @@ class replica_bitline(design.design):
self.add_layout_pin(text="gnd",
layer="metal1",
offset=dc_gnd_offset.scale(1,0),
width=drc["minwidth_metal1"],
width=self.m1_width,
height=self.delay_chain_offset.y)
def add_layout_pins(self):
""" Route the input and output signal """
en_offset = self.delay_chain_offset+self.delay_chain.get_pin("in").ur().rotate_scale(-1,1)
en_offset = self.dc_inst.get_pin("in").ll()
self.add_layout_pin(text="en",
layer="metal1",
offset=en_offset.scale(1,0),
width=drc["minwidth_metal1"],
width=self.m1_width,
height=en_offset.y)
out_offset = self.rbl_inv_offset+self.inv.get_pin("Z").ur().rotate_scale(-1,1) - vector(0,self.inv.width)
out_offset = self.rbl_inv_inst.get_pin("Z").ll()
self.add_layout_pin(text="out",
layer="metal1",
offset=out_offset.scale(1,0),
width=drc["minwidth_metal1"],
width=self.m1_width,
height=out_offset.y)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
pin = self.rbl_inv_inst.get_pin("A")
self.add_label_pin(text="bl[0]",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())
pin = self.dc_inst.get_pin("out")
self.add_label_pin(text="delayed_en",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())

View File

@ -1,8 +1,8 @@
import design
import debug
from tech import drc
from tech import drc, info
from vector import vector
from contact import contact
import contact
from ptx import ptx
from globals import OPTS
@ -26,137 +26,120 @@ class single_level_column_mux(design.design):
def create_layout(self):
# This is not instantiated and used for calculations only.
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.well_contact = contact(layer_stack=("active", "contact", "metal1"))
self.create_ptx()
self.add_ptx()
self.width = self.bitcell.width
# The height is bigger than necessary.
self.height = 2*self.nmos.height
self.height = self.nmos2.uy()
self.connect_poly()
self.connect_to_bitlines()
self.add_gnd_rail()
self.add_well_contacts()
self.add_bitline_pins()
self.connect_bitlines()
self.add_wells()
def add_bitline_pins(self):
""" Add the top and bottom pins to this cell """
bl_pos = vector(self.bitcell.get_pin("BL").lx(), 0)
br_pos = vector(self.bitcell.get_pin("BR").lx(), 0)
pin_height = 2*self.m2_width
# bl and br
self.add_layout_pin(text="bl",
layer="metal2",
offset=bl_pos + vector(0,self.height - pin_height),
height=pin_height)
self.add_layout_pin(text="br",
layer="metal2",
offset=br_pos + vector(0,self.height - pin_height),
height=pin_height)
# bl_out and br_out
self.add_layout_pin(text="bl_out",
layer="metal2",
offset=bl_pos,
height=pin_height)
self.add_layout_pin(text="br_out",
layer="metal2",
offset=br_pos,
height=pin_height)
def create_ptx(self):
def add_ptx(self):
""" Create the two pass gate NMOS transistors to switch the bitlines"""
# Adds nmos1,nmos2 to the module
self.nmos = ptx(width=self.ptx_width)
self.add_mod(self.nmos)
self.nmos1_position = vector(drc["minwidth_metal1"], drc["poly_extend_active"]) \
- vector([drc["well_enclosure_active"]]*2)
self.add_inst(name="mux_tx1",
mod=self.nmos,
offset=self.nmos1_position)
# Space it in the center
nmos1_position = self.nmos.active_offset.scale(0,1) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0)
self.nmos1=self.add_inst(name="mux_tx1",
mod=self.nmos,
offset=nmos1_position)
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
nmos2_to_nmos1 = vector(self.nmos.active_width,
self.nmos.active_height + drc["minwidth_poly"] + 2*drc["poly_extend_active"])
self.nmos2_position = self.nmos1_position + nmos2_to_nmos1
self.add_inst(name="mux_tx2",
mod=self.nmos,
offset=self.nmos2_position)
# This aligns it directly above the other tx with gates abutting
nmos2_position = nmos1_position + vector(0,self.nmos.active_height + self.poly_space)
self.nmos2=self.add_inst(name="mux_tx2",
mod=self.nmos,
offset=nmos2_position)
self.connect_inst(["br", "sel", "br_out", "gnd"])
def connect_poly(self):
""" Connect the poly gate of the two pass transistors """
self.poly_offset = self.nmos1_position + self.nmos.poly_positions[0] \
+ vector(0,self.nmos.poly_height)
width=self.nmos2_position.x - self.nmos1_position.x + drc["minwidth_poly"]
height=self.nmos2.get_pin("G").uy() - self.nmos1.get_pin("G").by()
self.add_layout_pin(text="col_addr",
layer="poly",
offset=self.poly_offset,
width=width,
height=drc["minwidth_poly"])
def connect_to_bitlines(self):
""" """
# place the contact at the top of the src/drain
offset = self.nmos1_position + vector(self.nmos.active_contact_positions[0].x + 0.5*self.m1m2_via.contact_width
+ 3 * (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width),
self.nmos.active_position.y + self.nmos.active_height - self.m1m2_via.height)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
bl_offset = vector(self.bitcell.get_pin("BL").lx(),self.height)
self.add_layout_pin(text="bl",
layer="metal2",
offset=bl_offset - vector(0, 2*drc["minwidth_metal2"]),
width=drc["minwidth_metal2"],
height=2*drc["minwidth_metal2"])
# draw an enclosing rectangle for the small jog
start = offset + vector(0.5*self.m1m2_via.width,0.5*self.m1m2_via.height)
end = self.get_pin("bl").bc()
mid1 = vector(start.x,0.5*(start.y+end.y))
mid2 = vector(end.x,mid1.y)
self.add_path("metal2",[start,mid1,mid2,end])
# place the contact at the bottom of the src/drain
offset = self.nmos1_position + self.nmos.active_contact_positions[1]
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
self.add_rect(layer="metal2",
offset=[self.bitcell.get_pin("BL").lx(), offset.y],
width=(offset.x - self.bitcell.get_pin("BL").lx() + 2*drc["minwidth_metal2"]),
height=drc["minwidth_metal2"])
self.add_layout_pin(text="bl_out",
layer="metal2",
offset=[self.bitcell.get_pin("BL").lx(), 0],
width=drc["minwidth_metal2"],
height=drc["minwidth_metal2"] + offset.y)
BL_out_position = vector(self.bitcell.get_pin("BL").lx()- 0.5*self.m1m2_via.width, 0)
offset=self.nmos1.get_pin("G").ll(),
height=height)
# place the contact at the top of the src/drain
offset = self.nmos2_position + vector(self.nmos.active_contact_positions[1].x - 0.5*self.m1m2_via.contact_width
- 2 * (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width),
self.nmos.active_position.y + self.nmos.active_height - self.m1m2_via.height)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
br_offset = vector(self.bitcell.get_pin("BR").lx(),self.height) - vector(0,2*drc["minwidth_metal2"])
self.add_layout_pin(text="br",
layer="metal2",
offset=br_offset,
width=drc["minwidth_metal2"],
height=2*drc["minwidth_metal2"])
def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """
# These are on metal2
bl_pin = self.get_pin("bl")
br_pin = self.get_pin("br")
bl_out_pin = self.get_pin("bl_out")
br_out_pin = self.get_pin("br_out")
# draw an enclosing rectangle for the small jog
ll = vector(min(offset.x,br_offset.x),min(offset.y,br_offset.y))
ur = vector(max(offset.x+self.m1m2_via.width,br_offset.x+drc["minwidth_metal2"]),
max(offset.y+self.m1m2_via.height,br_offset.y+2*drc["minwidth_metal2"]))
self.add_rect(layer="metal2",
offset=ll,
width=ur.x-ll.x,
height=ur.y-ll.y)
# These are on metal1
nmos1_s_pin = self.nmos1.get_pin("S")
nmos1_d_pin = self.nmos1.get_pin("D")
nmos2_s_pin = self.nmos2.get_pin("S")
nmos2_d_pin = self.nmos2.get_pin("D")
# place the contact in the bottom
offset = self.nmos2_position + self.nmos.active_contact_positions[0]
BR_out_position = vector(self.bitcell.get_pin("BR").lx(), 0)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
self.add_rect(layer="metal2",
offset=offset,
width=BR_out_position.x - offset.x,
height=drc["minwidth_metal2"])
self.add_layout_pin(text="br_out",
layer="metal2",
offset=[BR_out_position.x, 0],
height=offset.y+ drc["minwidth_metal2"])
# Add vias to bl, br_out, nmos2/S, nmos1/D
self.add_via_center(layers=("metal1","via1","metal2"),
offset=bl_pin.bc())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=br_out_pin.uc())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=nmos2_s_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=nmos1_d_pin.center())
# bl -> nmos2/D on metal1
# bl_out -> nmos2/S on metal2
self.add_path("metal1",[bl_pin.ll(), vector(nmos2_d_pin.cx(),bl_pin.by()), nmos2_d_pin.center()])
# halfway up, move over
mid1 = bl_out_pin.uc().scale(1,0.5)+nmos2_s_pin.bc().scale(0,0.5)
mid2 = bl_out_pin.uc().scale(0,0.5)+nmos2_s_pin.bc().scale(1,0.5)
self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos2_s_pin.bc()])
# br -> nmos1/D on metal2
# br_out -> nmos1/S on metal1
self.add_path("metal1",[br_out_pin.uc(), vector(nmos1_s_pin.cx(),br_out_pin.uy()), nmos1_s_pin.center()])
# halfway up, move over
mid1 = br_pin.bc().scale(1,0.5)+nmos1_d_pin.uc().scale(0,0.5)
mid2 = br_pin.bc().scale(0,0.5)+nmos1_d_pin.uc().scale(1,0.5)
self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos1_d_pin.uc()])
def add_gnd_rail(self):
""" Add the gnd rails through the cell to connect to the bitcell array """
gnd_pins = self.bitcell.get_pins("gnd")
for gnd_pin in gnd_pins:
# only use vertical gnd pins that span the whole cell
@ -165,9 +148,12 @@ class single_level_column_mux(design.design):
self.add_layout_pin(text="gnd",
layer="metal2",
offset=gnd_position,
height=self.get_pin("bl").uy())
height=self.height)
def add_wells(self):
""" Add a well and implant over the whole cell. Also, add the pwell contact (if it exists) """
def add_well_contacts(self):
# find right most gnd rail
gnd_pins = self.bitcell.get_pins("gnd")
right_gnd = None
@ -176,28 +162,31 @@ class single_level_column_mux(design.design):
right_gnd = gnd_pin
# Add to the right (first) gnd rail
m1m2_offset = right_gnd.ll() + vector(-0.5*self.m1m2_via.width,self.nmos.poly_height/2)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=m1m2_offset)
active_offset = right_gnd.ll() + vector(-self.m1m2_via.width,self.nmos.poly_height/2)
self.add_contact(layers=("active", "contact", "metal1"),
offset=active_offset)
m1m2_offset = right_gnd.bc() + vector(0,0.5*self.nmos.poly_height)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=m1m2_offset)
active_offset = right_gnd.bc() + vector(0,0.5*self.nmos.poly_height)
self.add_via_center(layers=("active", "contact", "metal1"),
offset=active_offset)
offset_implant = active_offset + vector([drc["implant_to_contact"]]*2).scale(-1,-1)
implant_width = 2*drc["implant_to_contact"] + self.well_contact.width
implant_height = 2*drc["implant_to_contact"] + self.well_contact.height
# implant must surround the active area
active_correct = vector(contact.well.width,contact.well.height).scale(0.5,0.5)
offset_implant = active_offset - vector([drc["implant_to_contact"]]*2) - active_correct
implant_width = 2*drc["implant_to_contact"] + contact.well.width
implant_height = 2*drc["implant_to_contact"] + contact.well.height
self.add_rect(layer="pimplant",
offset=offset_implant,
width=implant_width,
height=implant_height)
offset_well = self.nmos1_position + vector(self.nmos.width, 0)
self.add_rect(layer="pwell",
offset=offset_well,
width=offset_implant.x + implant_width + drc["well_enclosure_active"] - offset_well.x,
height=self.nmos2_position.y)
# Add a well around the whole cell
if info["has_pwell"]:
self.add_rect(layer="pwell",
offset=vector(0,0),
width=self.width + contact.well.width + drc["well_enclosure_active"],
height=self.height)
self.add_rect(layer="vtg",
offset=offset_well,
width=offset_implant.x + implant_width + drc["well_enclosure_active"] - offset_well.x,
height=self.nmos2_position.y)
offset=vector(0,0),
width=self.width + contact.well.width,
height=self.height)

View File

@ -1,7 +1,7 @@
from math import log
import design
from single_level_column_mux import single_level_column_mux
from contact import contact
import contact
from tech import drc
import debug
import math
@ -46,15 +46,12 @@ class single_level_column_mux_array(design.design):
tx_size=8)
self.add_mod(self.mux)
# This is not instantiated and used for calculations only.
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
def setup_layout_constants(self):
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
self.width = self.columns * self.mux.width
self.m1_pitch = self.m1m2_via.width + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m1_pitch = contact.m1m2.width + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
# To correct the offset between M1 and M2 via enclosures
self.offset_fix = vector(0,0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]))
# one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br
@ -118,31 +115,26 @@ class single_level_column_mux_array(design.design):
layer="metal1",
offset=offset,
width=self.mux.width * self.columns,
height=self.m1m2_via.width)
height=contact.m1m2.width)
def add_vertical_poly_rail(self):
""" Connect the poly to the address rails """
# Offset to the first transistor gate in the pass gate
nmos_offset = (self.mux.nmos1_position + self.mux.nmos.poly_positions[0]).scale(1,0)
for col in range(self.columns):
# which select bit should this column connect to depends on the position in the word
sel_index = col % self.words_per_row
# Add the column x offset to find the right select bit
gate_offset = nmos_offset + vector(col * self.mux.width , 0)
gate_offset = self.mux_inst[col].get_pin("col_addr").bc()
# height to connect the gate to the correct horizontal row
sel_height = self.get_pin("sel[{}]".format(sel_index)).by()
# use the y offset from the sel pin and the x offset from the gate
offset = vector(gate_offset.x,self.get_pin("sel[{}]".format(sel_index)).by())
self.add_rect(layer="poly",
offset=offset,
width=drc["minwidth_poly"],
height=self.route_height - sel_height)
offset = vector(gate_offset.x,self.get_pin("sel[{}]".format(sel_index)).cy())
# Add the poly contact with a shift to account for the rotation
self.add_contact(layers=("metal1", "contact", "poly"),
offset=offset + vector(self.m1m2_via.height,0),
rotate=90)
self.add_via_center(layers=("metal1", "contact", "poly"),
offset=offset,
rotate=90)
self.add_path("poly", [offset, gate_offset])
def route_bitlines(self):
""" Connect the output bit-lines to form the appropriate width mux """
@ -157,7 +149,7 @@ class single_level_column_mux_array(design.design):
# Create the metal1 to connect the n-way mux output from the pass gate
# These will be located below the select lines. Yes, these are M2 width
# to ensure vias are enclosed and M1 min width rules.
width = self.m1m2_via.width + self.mux.width * (self.words_per_row - 1)
width = contact.m1m2.width + self.mux.width * (self.words_per_row - 1)
self.add_rect(layer="metal1",
offset=bl_out_offset,
width=width,
@ -182,7 +174,7 @@ class single_level_column_mux_array(design.design):
# This via is on the right of the wire
self.add_via(layers=("metal1", "via1", "metal2"),
offset=bl_out_offset + vector(self.m1m2_via.height,0),
offset=bl_out_offset + vector(contact.m1m2.height,0),
rotate=90)
# This via is on the left of the wire
self.add_via(layers=("metal1", "via1", "metal2"),
@ -197,7 +189,7 @@ class single_level_column_mux_array(design.design):
height=self.route_height-bl_out_offset.y)
# This via is on the right of the wire
self.add_via(layers=("metal1", "via1", "metal2"),
offset=bl_out_offset + vector(self.m1m2_via.height,0),
offset=bl_out_offset + vector(contact.m1m2.height,0),
rotate=90)
self.add_rect(layer="metal2",
offset=br_out_offset,

View File

@ -3,7 +3,7 @@ from tech import drc, spice
import debug
import design
from math import log,sqrt,ceil
from contact import contact
import contact
from bank import bank
import datetime
import getpass
@ -48,26 +48,17 @@ class sram(design.design):
design.design.__init__(self, name)
# These aren't for instantiating, but we use them to get the dimensions
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
# For different layer width vias
self.m2m3_offset_fix = vector(0,0.5*(drc["minwidth_metal3"]-drc["minwidth_metal2"]))
self.m2m3_offset_fix = vector(0,0.5*(self.m3_width-self.m2_width))
self.m1_width = drc["minwidth_metal1"]
self.m2_width = drc["minwidth_metal2"]
self.m3_width = drc["minwidth_metal3"]
# M1/M2 routing pitch is based on contacted pitch of the biggest layer
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.m3_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space,self.m2_space)
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
self.m3_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
self.control_size = 6
self.bank_to_bus_distance = 5*drc["minwidth_metal3"]
self.bank_to_bus_distance = 5*self.m3_width
self.compute_sizes()
self.add_pins()
@ -108,7 +99,7 @@ class sram(design.design):
self.bank_addr_size = self.col_addr_size + self.row_addr_size
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
debug.info(0,"Words per row: {}".format(self.words_per_row))
debug.info(1,"Words per row: {}".format(self.words_per_row))
def estimate_words_per_row(self,tentative_num_cols, word_size):
"""This provides a heuristic rounded estimate for the number of words
@ -763,7 +754,7 @@ class sram(design.design):
self.power_rail_width = self.bank.vdd_rail_width
# Leave some extra space for the pitch
self.power_rail_pitch = self.bank.vdd_rail_width + 2*drc["metal3_to_metal3"]
self.power_rail_pitch = self.bank.vdd_rail_width + 2*self.m3_space
@ -860,6 +851,10 @@ class sram(design.design):
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
if self.num_banks==1: return
for n in self.control_bus_names:
@ -882,9 +877,9 @@ class sram(design.design):
# Control logic is placed to the left of the blank even with the
# decoder bottom. A small gap is in the x-dimension.
control_gap = 2*drc["minwidth_metal3"]
control_gap = 2*self.m3_width
pos = vector(-control_gap,
self.bank.row_decoder_inst.by() + 2*drc["minwidth_metal3"])
self.bank.row_decoder_inst.by() + 2*self.m3_width)
self.add_control_logic(position=pos,
rotate=90)

View File

@ -1,59 +0,0 @@
#!/usr/bin/env python2.7
"""
Run regresion tests on a parameterized nand_2. This module doesn't
generate multi_finger 2_input nand gate. It generate only the minimum
size 2_input nand gate that is nmos_width=2*tech.drc[minwidth_tx].
"""
import unittest
from testutils import header
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
import verify
import sys
OPTS = globals.OPTS
#@unittest.skip("SKIPPING 04_nand_2_test")
class nand_2_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 nand_2
import tech
debug.info(2, "Checking 2-input nand gate")
tx = nand_2.nand_2(nmos_width=2 * tech.drc["minwidth_tx"])
self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, tx):
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
tx.sp_write(tempspice)
tx.gds_write(tempgds)
self.assertFalse(verify.run_drc(tx.name, tempgds))
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
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()

View File

@ -26,7 +26,7 @@ class pinv_test(unittest.TestCase):
import tech
debug.info(2, "Checking 10x inverter")
tx = pinv.pinv(size=10)
tx = pinv.pinv(size=8)
self.local_check(tx)
OPTS.check_lvsdrc = True

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python2.7
"""
Run regression tests on a parameterized nand 2. This module doesn't
generate multi_finger 2_input nand gate. It generate only the minimum
size 2_input nand gate that is nmos_width=2*tech.drc[minwidth_tx].
generate a multi_finger 2-input nand gate. It generates only a minimum
size 2-input nand gate.
"""
import unittest
@ -16,7 +16,7 @@ import sys
OPTS = globals.OPTS
#@unittest.skip("SKIPPING 04_nand_2_test")
#@unittest.skip("SKIPPING 04_pnand2_test")
class pnand2_test(unittest.TestCase):

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python2.7
"""
Run regresion tests on a parameterized nand_3.
This module doesn't generate multi_finger 3_input nand gate.
It generate only the minimum size 3_input nand gate that is nmos_width=3*tech.drc[minwidth_tx].
Run regresion tests on a parameterized pnand3.
This module doesn't generate a multi-finger 3-input nand gate.
It generates only a minimum size 3-input nand gate.
"""
import unittest
@ -15,19 +15,19 @@ import verify
OPTS = globals.OPTS
#@unittest.skip("SKIPPING 04_nand_3_test")
class nand_3_test(unittest.TestCase):
#@unittest.skip("SKIPPING 04_pnand3_test")
class pnand3_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 nand_3
import pnand3
import tech
debug.info(2, "Checking 3-input nand gate")
tx = nand_3.nand_3(nmos_width=3 * tech.drc["minwidth_tx"])
tx = pnand3.pnand3(size=1)
self.local_check(tx)
OPTS.check_lvsdrc = True

View File

@ -1,9 +1,10 @@
#!/usr/bin/env python2.7
"""
Run regresion tests on a parameterized nor_2
This module doesn't generate multi_finger 2_input nor gate
It generate only the minimum size 2_input nor gate that is nmos_width=2*tech.drc[minwidth_tx]
Run regression tests on a parameterized nor 2. This module doesn't
generate a multi_finger 2-input nor gate. It generates only a minimum
size 2-input nor gate.
"""
import unittest
from testutils import header
import sys,os
@ -15,25 +16,26 @@ import sys
OPTS = globals.OPTS
#@unittest.skip("SKIPPING 04_nor_2_test")
#@unittest.skip("SKIPPING 04_pnor2_test")
class nor_2_test(unittest.TestCase):
class pnor2_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 nor_2
import pnor2
import tech
debug.info(2, "Checking 2-input nor gate")
tx = nor_2.nor_2()
tx = pnor2.pnor2(size=1)
self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, tx):
tempspice = OPTS.openram_temp + "temp.sp"
@ -44,7 +46,7 @@ class nor_2_test(unittest.TestCase):
self.assertFalse(verify.run_drc(tx.name, tempgds))
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
os.remove(tempspice)
os.remove(tempgds)

View File

@ -25,7 +25,7 @@ class precharge_test(unittest.TestCase):
import tech
debug.info(2, "Checking precharge")
tx = precharge.precharge(name="precharge_driver", ptx_width=tech.drc["minwidth_tx"])
tx = precharge.precharge(name="precharge_driver", size=1)
self.local_check(tx)
OPTS.check_lvsdrc = True

View File

@ -26,7 +26,7 @@ class precharge_test(unittest.TestCase):
import tech
debug.info(2, "Checking 3 column precharge")
pc = precharge_array.precharge_array(columns=3, ptx_width=tech.drc["minwidth_tx"], beta=2)
pc = precharge_array.precharge_array(columns=3)
self.local_check(pc)
OPTS.check_lvsdrc = True

View File

@ -26,8 +26,8 @@ class multi_bank_test(unittest.TestCase):
self.local_check(a)
debug.info(1, "Two way column mux")
#a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="bank2")
#self.local_check(a)
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="bank2")
self.local_check(a)
debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="bank3")

View File

@ -50,27 +50,26 @@ class timing_sram_test(unittest.TestCase):
loads = [tech.spice["FF_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data,slews,loads)
if OPTS.tech_name == "freepdk45":
golden_data = {'read1_power': 0.025833000000000002,
'read0_power': 0.026039,
'write0_power': 0.024105,
'delay1': [0.047506],
'delay0': [0.13799999999999998],
golden_data = {'read1_power': 0.0296933,
'read0_power': 0.029897899999999998,
'write0_power': 0.0258029,
'delay1': [0.049100700000000004],
'delay0': [0.13766139999999996],
'min_period': 0.322,
'write1_power': 0.024214,
'slew0': [0.026966],
'slew1': [0.019338]}
'write1_power': 0.0260398,
'slew0': [0.0265264],
'slew1': [0.0195507]}
elif OPTS.tech_name == "scn3me_subm":
golden_data = {'read1_power': 3.1765,
'read0_power': 3.1929,
'write0_power': 2.874,
'delay1': [0.8900045999999999],
'delay0': [1.9975000000000003],
'min_period': 5.781,
'write1_power': 2.6611,
'slew0': [1.2993000000000001],
'slew1': [0.9903856]}
golden_data = {'read1_power': 4.443,
'read0_power': 4.4712,
'write0_power': 3.0032,
'delay1': [0.8596608],
'delay0': [1.9534000000000002],
'min_period': 5.625,
'write1_power': 2.8086,
'slew0': [1.2982],
'slew1': [0.9909933]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -46,27 +46,26 @@ class timing_sram_test(unittest.TestCase):
loads = [tech.spice["FF_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data,slews,loads)
if OPTS.tech_name == "freepdk45":
golden_data = {'read1_power': 0.02527215,
'read0_power': 0.02573022,
'write0_power': 0.02237065,
'delay1': [0.04867785],
'delay0': [0.1423512],
golden_data = {'read1_power': 0.02813208,
'read0_power': 0.02856409,
'write0_power': 0.02578581,
'delay1': [0.050279449999999996],
'delay0': [0.1417553],
'min_period': 0.332,
'write1_power': 0.02152122,
'slew0': [0.0273352],
'slew1': [0.021216870000000002]}
'write1_power': 0.02516402,
'slew0': [0.02729188],
'slew1': [0.02057544]}
elif OPTS.tech_name == "scn3me_subm":
golden_data = {'read1_power': 3.244839,
'read0_power': 3.088234,
'write0_power': 2.6857420000000003,
'delay1': [0.9200643],
'delay0': [2.0509399999999998],
golden_data = {'read1_power': 4.324345,
'read0_power': 4.168978,
'write0_power': 2.828746,
'delay1': [0.8929376],
'delay0': [2.01039],
'min_period': 6.563,
'write1_power': 2.378355,
'slew0': [1.342019],
'slew1': [1.040885]}
'write1_power': 2.526374,
'slew0': [1.342036],
'slew1': [1.041426]}
else:
self.assertTrue(False) # other techs fail

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,7 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 692.2795;
area : 696.39825;
bus(DATA){
bus_type : DATA;
@ -92,10 +92,10 @@ cell (sram_2_16_1_freepdk45){
internal_power(){
when : "OEb & !clk";
rise_power(scalar){
values("0.0287643");
values("0.0308667");
}
fall_power(scalar){
values("0.0284106");
values("0.0304125");
}
}
timing(){
@ -129,10 +129,10 @@ cell (sram_2_16_1_freepdk45){
internal_power(){
when : "!OEb & !clk";
rise_power(scalar){
values("0.0320149");
values("0.0362061");
}
fall_power(scalar){
values("0.0322925");
values("0.0364614");
}
}
timing(){
@ -140,23 +140,23 @@ cell (sram_2_16_1_freepdk45){
related_pin : "clk";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.046, 0.046, 0.053",\
"0.046, 0.047, 0.054",\
"0.051, 0.052, 0.059");
values("0.047, 0.048, 0.055",\
"0.048, 0.049, 0.056",\
"0.053, 0.054, 0.061");
}
cell_fall(CELL_TABLE) {
values("0.142, 0.143, 0.152",\
"0.143, 0.144, 0.152",\
"0.148, 0.149, 0.158");
values("0.143, 0.144, 0.152",\
"0.144, 0.145, 0.153",\
"0.149, 0.15, 0.158");
}
rise_transition(CELL_TABLE) {
values("0.014, 0.015, 0.027",\
"0.014, 0.015, 0.027",\
"0.014, 0.015, 0.027");
"0.014, 0.016, 0.027");
}
fall_transition(CELL_TABLE) {
values("0.019, 0.02, 0.036",\
"0.019, 0.02, 0.036",\
values("0.019, 0.02, 0.035",\
"0.019, 0.02, 0.035",\
"0.019, 0.02, 0.036");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,7 @@ cell (sram_2_16_1_scn3me_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 89304.3;
area : 90431.64;
bus(DATA){
bus_type : DATA;
@ -92,10 +92,10 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){
when : "OEb & !clk";
rise_power(scalar){
values("3.1588");
values("3.3427");
}
fall_power(scalar){
values("3.4922");
values("3.6867");
}
}
timing(){
@ -129,10 +129,10 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){
when : "!OEb & !clk";
rise_power(scalar){
values("3.9389");
values("5.2453");
}
fall_power(scalar){
values("3.9642");
values("5.2708");
}
}
timing(){
@ -140,24 +140,24 @@ cell (sram_2_16_1_scn3me_subm){
related_pin : "clk";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.542, 0.626, 1.298",\
"0.545, 0.628, 1.304",\
"0.594, 0.674, 1.35");
values("0.509, 0.592, 1.265",\
"0.512, 0.594, 1.271",\
"0.561, 0.641, 1.317");
}
cell_fall(CELL_TABLE) {
values("1.532, 1.634, 2.6",\
"1.536, 1.639, 2.607",\
"1.587, 1.69, 2.657");
values("1.449, 1.549, 2.511",\
"1.454, 1.554, 2.518",\
"1.504, 1.606, 2.568");
}
rise_transition(CELL_TABLE) {
values("0.191, 0.337, 1.884",\
"0.193, 0.338, 1.885",\
"0.195, 0.341, 1.884");
values("0.19, 0.335, 1.887",\
"0.191, 0.336, 1.886",\
"0.193, 0.339, 1.886");
}
fall_transition(CELL_TABLE) {
values("0.255, 0.448, 2.467",\
"0.256, 0.447, 2.468",\
"0.256, 0.447, 2.454");
values("0.282, 0.465, 2.464",\
"0.283, 0.465, 2.463",\
"0.282, 0.465, 2.455");
}
}
}
@ -308,20 +308,20 @@ cell (sram_2_16_1_scn3me_subm){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(scalar) {
values("4.5315");
values("4.375");
}
fall_constraint(scalar) {
values("4.5315");
values("4.375");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("9.063");
values("8.75");
}
fall_constraint(scalar) {
values("9.063");
values("8.75");
}
}
}

View File

@ -74,7 +74,7 @@ cell (sram_2_16_1_scn3me_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 89304.3;
area : 90431.64;
bus(DATA){
bus_type : DATA;
@ -140,14 +140,14 @@ cell (sram_2_16_1_scn3me_subm){
related_pin : "clk";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.553, 0.6, 1.041",\
"0.553, 0.6, 1.041",\
"0.553, 0.6, 1.041");
values("0.561, 0.608, 1.049",\
"0.561, 0.608, 1.049",\
"0.561, 0.608, 1.049");
}
cell_fall(CELL_TABLE) {
values("0.553, 0.6, 1.041",\
"0.553, 0.6, 1.041",\
"0.553, 0.6, 1.041");
values("0.561, 0.608, 1.049",\
"0.561, 0.608, 1.049",\
"0.561, 0.608, 1.049");
}
rise_transition(CELL_TABLE) {
values("0.024, 0.081, 0.61",\

View File

@ -252,7 +252,7 @@ def run_pex(name, gds_name, sp_name, output=None):
implemented in gds_name and sp_name. """
debug.warning("PEX using magic not implemented.")
return 0
return 1
from tech import drc
if output == None:

View File

@ -1,11 +1,12 @@
from tech import drc, parameter
import debug
import design
import contact
from math import log
from math import sqrt
import math
from pinv import pinv
from nand_2 import nand_2
from pnand2 import pnand2
from vector import vector
from globals import OPTS
@ -43,14 +44,17 @@ class wordline_driver(design.design):
self.inv = pinv()
self.add_mod(self.inv)
self.nand2 = nand_2()
self.inv_no_output = pinv(route_output=False)
self.add_mod(self.inv_no_output)
self.nand2 = pnand2()
self.add_mod(self.nand2)
def offsets_of_gates(self):
self.x_offset0 = 2 * drc["minwidth_metal1"] + 5 * drc["metal1_to_metal1"]
self.x_offset0 = 2*self.m1_width + 5*self.m1_space
self.x_offset1 = self.x_offset0 + self.inv.width
self.x_offset2 = self.x_offset1 + self.nand2.width
@ -61,15 +65,15 @@ class wordline_driver(design.design):
# Wordline enable connection
en_pin=self.add_layout_pin(text="en",
layer="metal2",
offset=[drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"],0],
width=drc["minwidth_metal2"],
offset=[self.m1_width + 2*self.m1_space,0],
width=self.m2_width,
height=self.height)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=[0, -0.5*drc["minwidth_metal1"]],
offset=[0, -0.5*self.m1_width],
width=self.x_offset0,
height=drc["minwidth_metal1"])
height=self.m1_width)
for row in range(self.rows):
name_inv1 = "wl_driver_inv_en{}".format(row)
@ -78,7 +82,7 @@ class wordline_driver(design.design):
inv_nand2B_connection_height = (abs(self.inv.get_pin("Z").ll().y
- self.nand2.get_pin("B").ll().y)
+ drc["minwidth_metal1"])
+ self.m1_width)
if (row % 2):
y_offset = self.inv.height*(row + 1)
@ -99,7 +103,7 @@ class wordline_driver(design.design):
base_offset = vector(self.width, y_offset)
# Extend vdd and gnd of wordline_driver
yoffset = (row + 1) * self.inv.height - 0.5 * drc["minwidth_metal1"]
yoffset = (row + 1) * self.inv.height - 0.5 * self.m1_width
if (row % 2):
pin_name = "gnd"
else:
@ -109,15 +113,16 @@ class wordline_driver(design.design):
layer="metal1",
offset=[0, yoffset],
width=self.x_offset0,
height=drc["minwidth_metal1"])
height=self.m1_width)
# add inv1 based on the info above
inv1_inst=self.add_inst(name=name_inv1,
mod=self.inv,
mod=self.inv_no_output,
offset=name_inv1_offset,
mirror=inst_mirror )
self.connect_inst(["en", "en_bar[{0}]".format(row),
self.connect_inst(["en",
"en_bar[{0}]".format(row),
"vdd", "gnd"])
# add nand 2
nand_inst=self.add_inst(name=name_nand,
@ -147,25 +152,28 @@ class wordline_driver(design.design):
m1m2_via = self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=clk_offset)
# first inv to nand2 B
zl_pos = inv1_inst.get_pin("Z").lc()
zr_pos = inv1_inst.get_pin("Z").rc()
bl_pos = nand_inst.get_pin("B").lc()
br_pos = nand_inst.get_pin("B").rc()
self.add_path("metal1", [zl_pos, zr_pos, bl_pos, br_pos])
# first inv to nand2 A
zb_pos = inv1_inst.get_pin("Z").bc()
zu_pos = inv1_inst.get_pin("Z").uc()
bl_pos = nand_inst.get_pin("A").lc()
br_pos = nand_inst.get_pin("A").rc()
self.add_path("metal1", [zb_pos, zu_pos, bl_pos, br_pos])
# Nand2 out to 2nd inv
zl_pos = nand_inst.get_pin("Z").lc()
zr_pos = nand_inst.get_pin("Z").rc()
bl_pos = inv2_inst.get_pin("A").lc()
br_pos = inv2_inst.get_pin("A").rc()
self.add_path("metal1", [zl_pos, zr_pos, bl_pos, br_pos])
al_pos = inv2_inst.get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
# connect the decoder input pin to nand2 A
a_pin = nand_inst.get_pin("A")
a_pos = a_pin.lc()
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)
# connect the decoder input pin to nand2 B
b_pin = nand_inst.get_pin("B")
b_pos = b_pin.lc()
# needs to move down since B nand input is nearly aligned with A inv input
up_or_down = self.m2_space if row%2 else -self.m2_space
input_offset = vector(0,b_pos.y + up_or_down)
mid_via_offset = vector(clk_offset.x,input_offset.y) + vector(0.5*self.m2_width+self.m2_space+0.5*contact.m1m2.width,0)
# must under the clk line in M1
self.add_layout_pin_center_segment(text="in[{0}]".format(row),
layer="metal1",
@ -174,12 +182,10 @@ class wordline_driver(design.design):
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid_via_offset)
# now connect to the nand2 A
self.add_segment_center(layer="metal2",
start=mid_via_offset,
end=a_pos)
# now connect to the nand2 B
self.add_path("metal2", [mid_via_offset, b_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=a_pos + vector(0.5*m1m2_via.height,0),
offset=b_pos - vector(0.5*contact.m1m2.height,0),
rotate=90)
@ -188,7 +194,7 @@ class wordline_driver(design.design):
self.add_layout_pin_center_segment(text="wl[{0}]".format(row),
layer="metal1",
start=wl_offset,
end=wl_offset-vector(drc["minwidth_metal1"],0))
end=wl_offset-vector(self.m1_width,0))
def analytical_delay(self, slew, load=0):