mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'rewrite_ptx' into dev
This commit is contained in:
commit
18e748f3c0
347
compiler/bank.py
347
compiler/bank.py
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. """
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
181
compiler/pinv.py
181
compiler/pinv.py
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",\
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in New Issue