Merging branch with PrivateRAM dev

This commit is contained in:
Michael Timothy Grimes
2018-05-18 15:15:31 -07:00
84 changed files with 14871 additions and 13447 deletions
+354 -713
View File
File diff suppressed because it is too large Load Diff
+263
View File
@@ -0,0 +1,263 @@
import sys
from tech import drc, parameter
import debug
import design
import contact
from pinv import pinv
from pnand2 import pnand2
from pnor2 import pnor2
from vector import vector
from globals import OPTS
class bank_select(design.design):
"""Create a bank select signal that is combined with an array of
NOR+INV gates to gate the control signals in case of multiple
banks are created in upper level SRAM module
"""
def __init__(self, name="bank_select"):
design.design.__init__(self, name)
# Number of control lines in the bus
self.num_control_lines = 6
# The order of the control signals on the control bus:
self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"]
# These will be outputs of the gaters if this is multibank
self.control_signals = ["gated_"+str for str in self.input_control_signals]
self.add_pin_list(self.input_control_signals, "INPUT")
self.add_pin("bank_sel")
self.add_pin_list(self.control_signals, "OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.create_modules()
self.calculate_module_offsets()
self.add_modules()
self.route_modules()
self.DRC_LVS()
def create_modules(self):
""" Create modules for later instantiation """
# 1x Inverter
self.inv = pinv()
self.add_mod(self.inv)
# 4x Inverter
self.inv4x = pinv(4)
self.add_mod(self.inv4x)
self.nor2 = pnor2()
self.add_mod(self.nor2)
self.nand2 = pnand2()
self.add_mod(self.nand2)
def calculate_module_offsets(self):
# M1/M2 routing pitch is based on contacted pitch
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)
self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"]
self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"]
self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width)
self.xoffset_bank_sel_inv = 0
self.xoffset_inputs = 0
self.yoffset_maxpoint = self.num_control_lines * self.inv.height
# Include the M1 pitches for the supply rails and spacing
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
self.width = self.xoffset_inv + self.inv4x.width
def add_modules(self):
# bank select inverter
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
# bank select inverter (must be made unique if more than one OR)
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
mod=self.inv,
offset=[self.xoffset_bank_sel_inv, 0])
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
self.logic_inst = []
self.inv_inst = []
for i in range(self.num_control_lines):
input_name = self.input_control_signals[i]
gated_name = self.control_signals[i]
name_nand = "nand_{}".format(input_name)
name_nor = "nor_{}".format(input_name)
name_inv = "inv_{}".format(input_name)
y_offset = self.inv.height * i
if i%2:
y_offset += self.inv.height
mirror = "MX"
else:
mirror = ""
# These require OR (nor2+inv) gates since they are active low.
# (writes occur on clk low)
if input_name in ("clk_buf", "tri_en_bar"):
self.logic_inst.append(self.add_inst(name=name_nor,
mod=self.nor2,
offset=[self.xoffset_nor, y_offset],
mirror=mirror))
self.connect_inst([input_name,
"bank_sel_bar",
gated_name+"_temp_bar",
"vdd",
"gnd"])
# the rest are AND (nand2+inv) gates
else:
self.logic_inst.append(self.add_inst(name=name_nand,
mod=self.nand2,
offset=[self.xoffset_nand, y_offset],
mirror=mirror))
bank_sel_signal = "bank_sel"
self.connect_inst([input_name,
"bank_sel",
gated_name+"_temp_bar",
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x,
offset=[self.xoffset_inv, y_offset],
mirror=mirror))
self.connect_inst([gated_name+"_temp_bar",
gated_name,
"vdd",
"gnd"])
def route_modules(self):
# bank_sel is vertical wire
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
xoffset_bank_sel = bank_sel_inv_pin.lx()
bank_sel_line_pos = vector(xoffset_bank_sel, 0)
bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint)
self.add_path("metal2", [bank_sel_line_pos, bank_sel_line_end])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=bank_sel_inv_pin.lc())
# Route the pin to the left edge as well
bank_sel_pin_pos=vector(0, 0)
bank_sel_pin_end=vector(bank_sel_line_pos.x, bank_sel_pin_pos.y)
self.add_layout_pin_segment_center(text="bank_sel",
layer="metal3",
start=bank_sel_pin_pos,
end=bank_sel_pin_end)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=bank_sel_pin_end,
rotate=90)
# bank_sel_bar is vertical wire
bank_sel_bar_pin = self.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, 0),
height=2*self.inv.height)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=bank_sel_bar_pin.rc())
for i in range(self.num_control_lines):
logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i]
gated_name = self.control_signals[i]
if input_name in ("clk_buf", "tri_en_bar"):
xoffset_bank_signal = xoffset_bank_sel_bar
else:
xoffset_bank_signal = xoffset_bank_sel
# Connect the logic output to inverter input
pre = logic_inst.get_pin("Z").lc()
out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0)
in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0)
post = inv_inst.get_pin("A").rc()
self.add_path("metal1", [pre, out_position, in_position, post])
# Connect the logic B input to bank_sel/bank_sel_bar
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_pos,
rotate=90)
# Connect the logic A input to the input pin
logic_pos = logic_inst.get_pin("A").lc()
input_pos = vector(0,logic_pos.y)
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_segment_center(text=input_name,
layer="metal3",
start=input_pos,
end=logic_pos)
# Add output pins
out_pin = inv_inst.get_pin("Z")
self.add_layout_pin(text=gated_name,
layer=out_pin.layer,
offset=out_pin.ll(),
width=inv_inst.rx() - out_pin.lx(),
height=out_pin.height())
# Find the x offsets for where the vias/pins should be placed
a_xoffset = self.logic_inst[0].lx()
b_xoffset = self.inv_inst[0].lx()
for num in range(self.num_control_lines):
# Route both supplies
for n in ["vdd", "gnd"]:
supply_pin = self.inv_inst[num].get_pin(n)
supply_offset = supply_pin.ll().scale(0,1)
self.add_rect(layer="metal1",
offset=supply_offset,
width=self.width)
# Add pins in two locations
for xoffset in [a_xoffset, b_xoffset]:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos,
rotate=90)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
# Add vdd/gnd supply rails
gnd_pin = inv_inst.get_pin("gnd")
left_gnd_pos = vector(0, gnd_pin.cy())
self.add_layout_pin_segment_center(text="gnd",
layer="metal1",
start=left_gnd_pos,
end=gnd_pin.rc())
vdd_pin = inv_inst.get_pin("vdd")
left_vdd_pos = vector(0, vdd_pin.cy())
self.add_layout_pin_segment_center(text="vdd",
layer="metal1",
start=left_vdd_pos,
end=vdd_pin.rc())
+8 -8
View File
@@ -48,43 +48,43 @@ class bitcell(design.design):
def list_row_pins(self):
# Creates a list of row pins
row_pins = ["wl"]
row_pins = ["WL"]
return row_pins
def list_read_row_pins(self):
# Creates a list of row pins
row_pins = ["wl"]
row_pins = ["WL"]
return row_pins
def list_write_row_pins(self):
# Creates a list of row pins
row_pins = ["wl"]
row_pins = ["WL"]
return row_pins
def list_column_pins(self):
# Creates a list of column pins
column_pins = ["bl", "br"]
column_pins = ["BL", "BR"]
return column_pins
def list_read_column_pins(self):
# Creates a list of column pins
column_pins = ["bl"]
column_pins = ["BL"]
return column_pins
def list_read_bar_column_pins(self):
# Creates a list of column pins
column_pins = ["br"]
column_pins = ["BR"]
return column_pins
def list_write_column_pins(self):
# Creates a list of column pins
column_pins = ["bl"]
column_pins = ["BL"]
return column_pins
def list_write_bar_column_pins(self):
# Creates a list of column pins
column_pins = ["br"]
column_pins = ["BR"]
return column_pins
def analytical_power(self, proc, vdd, temp, load):
-19
View File
@@ -179,25 +179,6 @@ class bitcell_array(design.design):
#we do not consider the delay over the wire for now
return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay,
wl_to_cell_delay.slew)
def analytical_power(self, proc, vdd, temp, load):
"""Power of Bitcell array and bitline in nW."""
from tech import drc
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap()
bl_swing = 0.1 #This should probably be defined in the tech file or input
freq = spice["default_event_rate"]
bitline_dynamic = bl_swing*cell_load*vdd*vdd*freq #not sure if calculation is correct
#Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(proc, vdd, temp, load)
#Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
cell_power.leakage * self.column_size * self.row_size)
return total_power
def gen_wl_wire(self):
wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"])
File diff suppressed because it is too large Load Diff
+112 -123
View File
@@ -34,7 +34,7 @@ class delay_chain(design.design):
self.add_pins()
self.create_module()
self.route_inv()
self.route_inverters()
self.add_layout_pins()
self.DRC_LVS()
@@ -48,82 +48,69 @@ class delay_chain(design.design):
def create_module(self):
""" Add the inverter logical module """
self.create_inv_list()
self.inv = pinv(route_output=False)
self.add_mod(self.inv)
# half chain length is the width of the layout
# invs are stacked into 2 levels so input/output are close
# extra metal is for the gnd connection U
self.width = self.num_top_half * self.inv.width + 2*drc["metal1_to_metal1"] + 0.5*drc["minwidth_metal1"]
self.height = 2 * self.inv.height
self.height = len(self.fanout_list)*self.inv.height
self.width = (max(self.fanout_list)+1) * self.inv.width
self.add_inv_list()
self.add_inverters()
def create_inv_list(self):
"""
Generate a list of inverters. Each inverter has a stage
number and a flag indicating if it is a dummy load. This is
the order that they will get placed too.
"""
# First stage is always 0 and is not a dummy load
self.inv_list=[[0,False]]
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
for i in range(fanout_size-1):
# Add the dummy loads
self.inv_list.append([stage_num+1, True])
# Add the gate to drive the next stage
self.inv_list.append([stage_num+1, False])
def add_inv_list(self):
def add_inverters(self):
""" 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):
# First place the gates
if i < self.num_top_half:
# add top level that is upside down
inv_offset = vector(i * self.inv.width, 2 * self.inv.height)
inv_mirror="MX"
self.driver_inst_list = []
self.rightest_load_inst = {}
self.load_inst_map = {}
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
if stage_num % 2:
inv_mirror = "MX"
inv_offset = vector(0, (stage_num+1)* self.inv.height)
else:
# add bottom level from right to left
inv_offset = vector((self.num_inverters - i) * self.inv.width, 0)
inv_mirror="MY"
cur_inv=self.add_inst(name="dinv{}".format(i),
inv_mirror = "R0"
inv_offset = vector(0, stage_num * self.inv.height)
# Add the inverter
cur_driver=self.add_inst(name="dinv{}".format(stage_num),
mod=self.inv,
offset=inv_offset,
mirror=inv_mirror)
# keep track of the inverter instances so we can use them to get the pins
self.inv_inst_list.append(cur_inv)
self.driver_inst_list.append(cur_driver)
# Second connect them logically
cur_stage = self.inv_list[i][0]
next_stage = self.inv_list[i][0]+1
if i == 0:
input = "in"
# Hook up the driver
if stage_num+1==len(self.fanout_list):
stageout_name = "out"
else:
input = "s{}".format(cur_stage)
if i == self.num_inverters-1:
output = "out"
else:
output = "s{}".format(next_stage)
# if the gate is a dummy load don't connect the output
# else reset the counter
if self.inv_list[i][1]:
output = output+"n{0}".format(dummy_load_counter)
dummy_load_counter += 1
stageout_name = "dout_{}".format(stage_num+1)
if stage_num == 0:
stagein_name = "in"
else:
dummy_load_counter = 1
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())
stagein_name = "dout_{}".format(stage_num)
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
# Now add the dummy loads to the right
self.load_inst_map[cur_driver]=[]
for i in range(fanout_size):
inv_offset += vector(self.inv.width,0)
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num,i),
mod=self.inv,
offset=inv_offset,
mirror=inv_mirror)
# Fanout stage is always driven by driver and output is disconnected
disconnect_name = "n_{0}_{1}".format(stage_num,i)
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
# Keep track of all the loads to connect their inputs as a load
self.load_inst_map[cur_driver].append(cur_load)
else:
# Keep track of the last one so we can add the the wire later
self.rightest_load_inst[cur_driver]=cur_load
def add_route(self, pin1, pin2):
""" This guarantees that we route from the top to bottom row correctly. """
pin1_pos = pin1.center()
@@ -135,79 +122,81 @@ class delay_chain(design.design):
# 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):
def route_inverters(self):
""" Add metal routing for each of the fanout stages """
start_inv = end_inv = 0
for fanout in self.fanout_list:
# end inv number depends on the fan out number
end_inv = start_inv + fanout
start_inv_inst = self.inv_inst_list[start_inv]
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start_inv_inst.get_pin("Z").center()),
# 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
for i in range(len(self.driver_inst_list)):
inv = self.driver_inst_list[i]
for load in self.load_inst_map[inv]:
# Drop a via on each A pin
a_pin = load.get_pin("A")
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
offset=a_pin.center())
# Route an M3 horizontal wire to the furthest
z_pin = inv.get_pin("Z")
a_pin = inv.get_pin("A")
a_max = self.rightest_load_inst[inv].get_pin("A")
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=z_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
offset=z_pin.center())
self.add_path("metal3",[z_pin.center(), a_max.center()])
# Route Z to the A of the next stage
if i+1 < len(self.driver_inst_list):
z_pin = inv.get_pin("Z")
next_inv = self.driver_inst_list[i+1]
next_a_pin = next_inv.get_pin("A")
y_mid = (z_pin.cy() + next_a_pin.cy())/2
mid1_point = vector(z_pin.cx(), y_mid)
mid2_point = vector(next_a_pin.cx(), y_mid)
self.add_path("metal2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
def add_layout_pins(self):
""" Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
the top end with no input/output to obstruct. """
vdd_pin = self.inv.get_pin("vdd")
gnd_pin = self.inv.get_pin("gnd")
for i in range(3):
(offset,y_dir)=self.get_gate_offset(0, self.inv.height, i)
rail_width = self.num_top_half * self.inv.width
if i % 2:
self.add_layout_pin(text="vdd",
layer="metal1",
offset=offset + vdd_pin.ll().scale(1,y_dir),
width=rail_width,
height=drc["minwidth_metal1"])
else:
self.add_layout_pin(text="gnd",
layer="metal1",
offset=offset + gnd_pin.ll().scale(1,y_dir),
width=rail_width,
height=drc["minwidth_metal1"])
# Use the right most parts of the gnd rails and add a U connector
# We still have the two gnd pins, but it is an either-or connect
gnd_pins = self.get_pins("gnd")
gnd_start = gnd_pins[0].rc()
gnd_mid1 = gnd_start + vector(2*drc["metal1_to_metal1"],0)
gnd_end = gnd_pins[1].rc()
gnd_mid2 = gnd_end + vector(2*drc["metal1_to_metal1"],0)
#self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
self.add_path("metal1", [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
for driver in self.driver_inst_list:
vdd_pin = driver.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=vdd_pin.height())
gnd_pin = driver.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=gnd_pin.height())
# input is A pin of first inverter
a_pin = self.inv_inst_list[0].get_pin("A")
a_pin = self.driver_inst_list[0].get_pin("A")
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a_pin.center())
self.add_layout_pin(text="in",
layer="metal1",
offset=a_pin.ll(),
width=a_pin.width(),
height=a_pin.height())
layer="metal2",
offset=a_pin.ll().scale(1,0),
height=a_pin.cy())
# output is Z pin of last inverter
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=z_pin.lx())
# output is A pin of last load inverter
last_driver_inst = self.driver_inst_list[-1]
a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A")
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a_pin.center())
mid_point = vector(a_pin.cx()+3*self.m2_width,a_pin.cy())
self.add_path("metal2",[a_pin.center(), mid_point, mid_point.scale(1,0)])
self.add_layout_pin_segment_center(text="out",
layer="metal2",
start=mid_point,
end=mid_point.scale(1,0))
+1 -1
View File
@@ -10,7 +10,7 @@ class dff(design.design):
Memory address flip-flop
"""
pin_names = ["d", "q", "clk", "vdd", "gnd"]
pin_names = ["D", "Q", "clk", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"], layer["boundary"])
+47 -26
View File
@@ -11,7 +11,7 @@ class dff_array(design.design):
Unlike the data flops, these are never spaced out.
"""
def __init__(self, rows, columns, name=""):
def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""):
self.rows = rows
self.columns = columns
@@ -22,11 +22,11 @@ class dff_array(design.design):
c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, OPTS.dff)
self.ms = self.mod_dff("dff")
self.add_mod(self.ms)
self.dff = self.mod_dff("dff")
self.add_mod(self.dff)
self.width = self.columns * self.ms.width
self.height = self.rows * self.ms.height
self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height
self.create_layout()
@@ -37,13 +37,12 @@ class dff_array(design.design):
self.DRC_LVS()
def add_pins(self):
for row in range(self.rows):
for col in range(self.columns):
self.add_pin("din[{0}][{1}]".format(row,col))
for row in range(self.rows):
for col in range(self.columns):
self.add_pin("dout[{0}][{1}]".format(row,col))
#self.add_pin("dout_bar[{0}]".format(i))
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_din_name(y,x))
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_dout_name(y,x))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
@@ -54,21 +53,42 @@ class dff_array(design.design):
for x in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x)
if (y % 2 == 0):
base = vector(x*self.ms.width,y*self.ms.height)
base = vector(x*self.dff.width,y*self.dff.height)
mirror = "R0"
else:
base = vector(x*self.ms.width,(y+1)*self.ms.height)
base = vector(x*self.dff.width,(y+1)*self.dff.height)
mirror = "MX"
self.dff_insts[x,y]=self.add_inst(name=name,
mod=self.ms,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst(["din[{0}][{1}]".format(x,y),
"dout[{0}][{1}]".format(x,y),
self.connect_inst([self.get_din_name(y,x),
self.get_dout_name(y,x),
"clk",
"vdd",
"gnd"])
def get_din_name(self, row, col):
if self.columns == 1:
din_name = "din[{0}]".format(row)
elif self.rows == 1:
din_name = "din[{0}]".format(col)
else:
din_name = "din[{0}][{1}]".format(row,col)
return din_name
def get_dout_name(self, row, col):
if self.columns == 1:
dout_name = "dout[{0}]".format(row)
elif self.rows == 1:
dout_name = "dout[{0}]".format(col)
else:
dout_name = "dout[{0}][{1}]".format(row,col)
return dout_name
def add_layout_pins(self):
for y in range(self.rows):
@@ -91,23 +111,24 @@ class dff_array(design.design):
for y in range(self.rows):
for x in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("d")
debug.check(din_pin.layer=="metal2","DFF d pin not on metal2")
self.add_layout_pin(text="din[{0}][{1}]".format(x,y),
din_pin = self.dff_insts[x,y].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(y,x),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[x,y].get_pin("q")
debug.check(dout_pin.layer=="metal2","DFF q pin not on metal2")
self.add_layout_pin(text="dout[{0}][{1}]".format(x,y),
dout_pin = self.dff_insts[x,y].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(y,x),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
@@ -120,7 +141,7 @@ class dff_array(design.design):
else:
self.add_layout_pin(text="clk",
layer="metal3",
offset=clk_pin.ll().scale(0,1),
offset=vector(0,0),
width=self.width,
height=self.m3_width)
for x in range(self.columns):
@@ -133,9 +154,9 @@ class dff_array(design.design):
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
offset=clk_pin.center())
offset=clk_pin.center().scale(1,0))
def analytical_delay(self, slew, load=0.0):
return self.ms.analytical_delay(slew=slew, load=load)
return self.dff.analytical_delay(slew=slew, load=load)
+149
View File
@@ -0,0 +1,149 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from pinv import pinv
class dff_buf(design.design):
"""
This is a simple buffered DFF. The output is buffered
with two inverters, of variable size, to provide q
and qbar. This is to enable driving large fanout loads.
"""
def __init__(self, inv1_size=2, inv2_size=4, name=""):
if name=="":
name = "dff_buf_{0}_{1}".format(inv1_size, inv2_size)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, OPTS.dff)
self.dff = self.mod_dff("dff")
self.add_mod(self.dff)
self.inv1 = pinv(size=inv1_size,height=self.dff.height)
self.add_mod(self.inv1)
self.inv2 = pinv(size=inv2_size,height=self.dff.height)
self.add_mod(self.inv2)
self.width = self.dff.width + self.inv1.width + self.inv2.width
self.height = self.dff.height
self.create_layout()
def create_layout(self):
self.add_pins()
self.add_insts()
self.add_wires()
self.add_layout_pins()
self.DRC_LVS()
def add_pins(self):
self.add_pin("D")
self.add_pin("Q")
self.add_pin("Qb")
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def add_insts(self):
# Add the DFF
self.dff_inst=self.add_inst(name="dff_buf_dff",
mod=self.dff,
offset=vector(0,0))
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
# Add INV1 to the right
self.inv1_inst=self.add_inst(name="dff_buf_inv1",
mod=self.inv1,
offset=vector(self.dff_inst.rx(),0))
self.connect_inst(["qint", "Qb", "vdd", "gnd"])
# Add INV2 to the right
self.inv2_inst=self.add_inst(name="dff_buf_inv2",
mod=self.inv2,
offset=vector(self.inv1_inst.rx(),0))
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
def add_wires(self):
# Route dff q to inv1 a
q_pin = self.dff_inst.get_pin("Q")
a1_pin = self.inv1_inst.get_pin("A")
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
mid1 = vector(mid_x_offset, q_pin.cy())
mid2 = vector(mid_x_offset, a1_pin.cy())
self.add_path("metal3",
[q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=q_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
offset=a1_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a1_pin.center())
# Route inv1 z to inv2 a
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.dff_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.dff_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=vdd_pin.height())
clk_pin = self.dff_inst.get_pin("clk")
self.add_layout_pin(text="clk",
layer=clk_pin.layer,
offset=clk_pin.ll(),
width=clk_pin.width(),
height=clk_pin.height())
din_pin = self.dff_inst.get_pin("D")
self.add_layout_pin(text="D",
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.inv2_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Q",
layer="metal2",
offset=dout_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=dout_pin.center())
dout_pin = self.inv2_inst.get_pin("A")
self.add_layout_pin_rect_center(text="Qb",
layer="metal2",
offset=dout_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=dout_pin.center())
def analytical_delay(self, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
dff_delay=self.dff.analytical_delay(slew=slew, load=self.inv1.input_load())
inv1_delay = self.inv1.analytical_delay(slew=dff_delay.slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load)
return dff_delay + inv1_delay + inv2_delay
+191
View File
@@ -0,0 +1,191 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import dff_buf
class dff_buf_array(design.design):
"""
This is a simple row (or multiple rows) of flops.
Unlike the data flops, these are never spaced out.
"""
def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""):
self.rows = rows
self.columns = columns
if name=="":
name = "dff_array_{0}x{1}".format(rows, columns)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
self.dff = dff_buf.dff_buf(inv1_size, inv2_size)
self.add_mod(self.dff)
self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height
self.create_layout()
def create_layout(self):
self.add_pins()
self.create_dff_array()
self.add_layout_pins()
self.DRC_LVS()
def add_pins(self):
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_din_name(y,x))
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_dout_name(y,x))
self.add_pin(self.get_dout_bar_name(y,x))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def create_dff_array(self):
self.dff_insts={}
for y in range(self.rows):
for x in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x)
if (y % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height)
mirror = "R0"
else:
base = vector(x*self.dff.width,(y+1)*self.dff.height)
mirror = "MX"
self.dff_insts[x,y]=self.add_inst(name=name,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(y,x),
self.get_dout_name(y,x),
self.get_dout_bar_name(y,x),
"clk",
"vdd",
"gnd"])
def get_din_name(self, row, col):
if self.columns == 1:
din_name = "din[{0}]".format(row)
elif self.rows == 1:
din_name = "din[{0}]".format(col)
else:
din_name = "din[{0}][{1}]".format(row,col)
return din_name
def get_dout_name(self, row, col):
if self.columns == 1:
dout_name = "dout[{0}]".format(row)
elif self.rows == 1:
dout_name = "dout[{0}]".format(col)
else:
dout_name = "dout[{0}][{1}]".format(row,col)
return dout_name
def get_dout_bar_name(self, row, col):
if self.columns == 1:
dout_bar_name = "dout_bar[{0}]".format(row)
elif self.rows == 1:
dout_bar_name = "dout_bar[{0}]".format(col)
else:
dout_bar_name = "dout_bar[{0}][{1}]".format(row,col)
return dout_bar_name
def add_layout_pins(self):
xoffsets = []
for x in range(self.columns):
xoffsets.append(self.dff_insts[x,0].get_pin("gnd").lx())
for y in range(self.rows):
# Route both supplies
for n in ["vdd", "gnd"]:
supply_pin = self.dff_insts[0,y].get_pin(n)
supply_offset = supply_pin.ll()
self.add_rect(layer="metal1",
offset=supply_offset,
width=self.width)
# Add pins in two locations
for xoffset in xoffsets:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos,
rotate=90)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
for y in range(self.rows):
for x in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(y,x),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[x,y].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(y,x),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(y,x),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(),
height=dout_bar_pin.height())
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
if self.columns==1:
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin(text="clk",
layer="metal3",
offset=vector(0,2*self.m2_width),
width=self.width,
height=self.m3_width)
for x in range(self.columns):
clk_pin = self.dff_insts[x,0].get_pin("clk")
# Make a vertical strip for each column
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
offset=clk_pin.center().scale(1,0) + vector(0,2*self.m2_width))
def analytical_delay(self, slew, load=0.0):
return self.dff.analytical_delay(slew=slew, load=load)
+132
View File
@@ -0,0 +1,132 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from pinv import pinv
class dff_inv(design.design):
"""
This is a simple DFF with an inverted output. Some DFFs
do not have Qbar, so this will create it.
"""
def __init__(self, inv_size=1, name=""):
if name=="":
name = "dff_inv_{0}".format(inv_size)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, OPTS.dff)
self.dff = self.mod_dff("dff")
self.add_mod(self.dff)
self.inv1 = pinv(size=inv_size,height=self.dff.height)
self.add_mod(self.inv1)
self.width = self.dff.width + self.inv1.width
self.height = self.dff.height
self.create_layout()
def create_layout(self):
self.add_pins()
self.add_insts()
self.add_wires()
self.add_layout_pins()
self.DRC_LVS()
def add_pins(self):
self.add_pin("D")
self.add_pin("Q")
self.add_pin("Qb")
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def add_insts(self):
# Add the DFF
self.dff_inst=self.add_inst(name="dff_inv_dff",
mod=self.dff,
offset=vector(0,0))
self.connect_inst(["D", "Q", "clk", "vdd", "gnd"])
# Add INV1 to the right
self.inv1_inst=self.add_inst(name="dff_inv_inv1",
mod=self.inv1,
offset=vector(self.dff_inst.rx(),0))
self.connect_inst(["Q", "Qb", "vdd", "gnd"])
def add_wires(self):
# Route dff q to inv1 a
q_pin = self.dff_inst.get_pin("Q")
a1_pin = self.inv1_inst.get_pin("A")
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
mid1 = vector(mid_x_offset, q_pin.cy())
mid2 = vector(mid_x_offset, a1_pin.cy())
self.add_path("metal3",
[q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=q_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
offset=a1_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a1_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.dff_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.dff_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=vdd_pin.height())
clk_pin = self.dff_inst.get_pin("clk")
self.add_layout_pin(text="clk",
layer=clk_pin.layer,
offset=clk_pin.ll(),
width=clk_pin.width(),
height=clk_pin.height())
din_pin = self.dff_inst.get_pin("D")
self.add_layout_pin(text="D",
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_inst.get_pin("Q")
self.add_layout_pin_rect_center(text="Q",
layer=dout_pin.layer,
offset=dout_pin.center())
dout_pin = self.inv1_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Qb",
layer="metal2",
offset=dout_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=dout_pin.center())
def analytical_delay(self, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
dff_delay=self.dff.analytical_delay(slew=slew, load=self.inv1.input_load())
inv1_delay = self.inv1.analytical_delay(slew=dff_delay.slew, load=load)
return dff_delay + inv1_delay
+180
View File
@@ -0,0 +1,180 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import dff_inv
class dff_inv_array(design.design):
"""
This is a simple row (or multiple rows) of flops.
Unlike the data flops, these are never spaced out.
"""
def __init__(self, rows, columns, inv_size=2, name=""):
self.rows = rows
self.columns = columns
if name=="":
name = "dff_array_{0}x{1}".format(rows, columns)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
self.dff = dff_inv.dff_inv(inv_size)
self.add_mod(self.dff)
self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height
self.create_layout()
def create_layout(self):
self.add_pins()
self.create_dff_array()
self.add_layout_pins()
self.DRC_LVS()
def add_pins(self):
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_din_name(y,x))
for y in range(self.rows):
for x in range(self.columns):
self.add_pin(self.get_dout_name(y,x))
self.add_pin(self.get_dout_bar_name(y,x))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def create_dff_array(self):
self.dff_insts={}
for y in range(self.rows):
for x in range(self.columns):
name = "Xdff_r{0}_c{1}".format(y,x)
if (y % 2 == 0):
base = vector(x*self.dff.width,y*self.dff.height)
mirror = "R0"
else:
base = vector(x*self.dff.width,(y+1)*self.dff.height)
mirror = "MX"
self.dff_insts[x,y]=self.add_inst(name=name,
mod=self.dff,
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(y,x),
self.get_dout_name(y,x),
self.get_dout_bar_name(y,x),
"clk",
"vdd",
"gnd"])
def get_din_name(self, row, col):
if self.columns == 1:
din_name = "din[{0}]".format(row)
elif self.rows == 1:
din_name = "din[{0}]".format(col)
else:
din_name = "din[{0}][{1}]".format(row,col)
return din_name
def get_dout_name(self, row, col):
if self.columns == 1:
dout_name = "dout[{0}]".format(row)
elif self.rows == 1:
dout_name = "dout[{0}]".format(col)
else:
dout_name = "dout[{0}][{1}]".format(row,col)
return dout_name
def get_dout_bar_name(self, row, col):
if self.columns == 1:
dout_bar_name = "dout_bar[{0}]".format(row)
elif self.rows == 1:
dout_bar_name = "dout_bar[{0}]".format(col)
else:
dout_bar_name = "dout_bar[{0}][{1}]".format(row,col)
return dout_bar_name
def add_layout_pins(self):
for y in range(self.rows):
# Continous vdd rail along with label.
vdd_pin=self.dff_insts[0,y].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=self.width,
height=self.m1_width)
# Continous gnd rail along with label.
gnd_pin=self.dff_insts[0,y].get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=self.width,
height=self.m1_width)
for y in range(self.rows):
for x in range(self.columns):
din_pin = self.dff_insts[x,y].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(y,x),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[x,y].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(y,x),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
dout_bar_pin = self.dff_insts[x,y].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(y,x),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(),
height=dout_bar_pin.height())
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
if self.columns==1:
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin(text="clk",
layer="metal3",
offset=vector(0,0),
width=self.width,
height=self.m3_width)
for x in range(self.columns):
clk_pin = self.dff_insts[x,0].get_pin("clk")
# Make a vertical strip for each column
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
offset=clk_pin.center().scale(1,0))
def analytical_delay(self, slew, load=0.0):
return self.dff.analytical_delay(slew=slew, load=load)
+61 -38
View File
@@ -31,8 +31,11 @@ class hierarchical_decoder(design.design):
self.rows = rows
self.num_inputs = int(math.log(self.rows, 2))
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
self.create_layout()
self.offset_all_coordinates()
self.DRC_LVS()
def create_layout(self):
@@ -51,10 +54,15 @@ class hierarchical_decoder(design.design):
self.add_mod(self.nand2)
self.nand3 = pnand3()
self.add_mod(self.nand3)
self.add_decoders()
# CREATION OF PRE-DECODER
def add_decoders(self):
""" Create the decoders based on the number of pre-decodes """
# FIXME: Only add these if needed?
self.pre2_4 = pre2x4()
self.add_mod(self.pre2_4)
self.pre3_8 = pre3x8()
self.add_mod(self.pre3_8)
@@ -146,12 +154,11 @@ class hierarchical_decoder(design.design):
else:
nand_width = self.nand3.width
self.routing_width = self.metal2_pitch*self.total_number_of_predecoder_outputs
self.row_decoder_width = nand_width + self.routing_width + self.inv.width
self.row_decoder_height = self.inv.height * self.rows
# Calculates height and width of hierarchical decoder
self.height = self.predecoder_height + self.row_decoder_height
self.width = self.predecoder_width + self.routing_width
self.height = self.row_decoder_height
self.width = self.predecoder_width + self.routing_width + nand_width + self.inv.width
def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
@@ -166,12 +173,10 @@ class hierarchical_decoder(design.design):
""" Add a 2x4 predecoder """
if (self.num_inputs == 2):
base = vector(self.routing_width,0)
mirror = "RO"
base = vector(-self.pre2_4.width,0)
index_off1 = index_off2 = 0
else:
base= vector(self.routing_width+self.pre2_4.width, num * self.pre2_4.height)
mirror = "MY"
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
index_off1 = num * 2
index_off2 = num * 4
@@ -184,8 +189,7 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
mod=self.pre2_4,
offset=base,
mirror=mirror))
offset=base))
self.connect_inst(pins)
self.add_pre2x4_pins(num)
@@ -210,12 +214,11 @@ class hierarchical_decoder(design.design):
def add_pre3x8(self,num):
""" Add 3x8 numbered predecoder """
if (self.num_inputs == 3):
offset = vector(self.routing_width,0)
offset = vector(-self.pre_3_8.width,0)
mirror ="R0"
else:
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
offset = vector(self.routing_width+self.pre3_8.width, height)
mirror="MY"
offset = vector(-self.pre3_8.width, height)
# If we had 2x4 predecodes, those are used as the lower
# decode output bits
@@ -231,8 +234,7 @@ class hierarchical_decoder(design.design):
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
mod=self.pre3_8,
offset=offset,
mirror=mirror))
offset=offset))
self.connect_inst(pins)
# The 3x8 predecoders will be stacked, so use yoffset
@@ -300,11 +302,11 @@ class hierarchical_decoder(design.design):
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_off = nand_mod.height*row
y_dir = 1
mirror = "R0"
else:
y_off = self.predecoder_height + nand_mod.height*(row + 1)
y_off = nand_mod.height*(row + 1)
y_dir = -1
mirror = "MX"
@@ -337,7 +339,7 @@ class hierarchical_decoder(design.design):
inv_row_height = self.inv.height * (row + 1)
mirror = "MX"
y_dir = -1
y_off = self.predecoder_height + inv_row_height
y_off = inv_row_height
offset = vector(x_off,y_off)
self.inv_inst.append(self.add_inst(name=name,
@@ -403,7 +405,7 @@ class hierarchical_decoder(design.design):
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)
self.connect_rail_m3(index, pin)
for pre_num in range(self.no_of_pre3x8):
@@ -411,7 +413,7 @@ class hierarchical_decoder(design.design):
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_m3(index, pin)
@@ -443,26 +445,34 @@ class hierarchical_decoder(design.design):
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
for num in range(0,self.total_number_of_predecoder_outputs + self.rows):
# Find the x offsets for where the vias/pins should be placed
a_xoffset = self.inv_inst[0].lx()
b_xoffset = self.inv_inst[0].rx()
for num in range(0,self.rows):
# this will result in duplicate polygons for rails, but who cares
# use the inverter offset even though it will be the nand's too
(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)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_offset,
width=self.width,
height=drc["minwidth_metal1"])
# Route both supplies
for n in ["vdd", "gnd"]:
supply_pin = self.inv_inst[num].get_pin(n)
# route gnd
gnd_offset = gate_offset+self.inv.get_pin("gnd").ll().scale(1,y_dir)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_offset,
width=self.width,
height=drc["minwidth_metal1"])
# Add pins in two locations
for xoffset in [a_xoffset, b_xoffset]:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos,
rotate=90)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
# Copy the pins from the predecoders
for pre in self.pre2x4_inst + self.pre3x8_inst:
self.copy_layout_pin(pre, "vdd")
self.copy_layout_pin(pre, "gnd")
def connect_rail(self, rail_index, pin):
@@ -473,6 +483,19 @@ class hierarchical_decoder(design.design):
offset=rail_pos,
rotate=90)
def connect_rail_m3(self, rail_index, pin):
""" Connect the routing rail to the given metal1 pin """
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin.center(),
rotate=90)
self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=rail_pos,
rotate=90)
def analytical_delay(self, slew, load = 0.0):
# A -> out
+35 -29
View File
@@ -21,8 +21,6 @@ class hierarchical_predecode(design.design):
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell_height = self.mod_bitcell.height
def add_pins(self):
for k in range(self.number_of_inputs):
@@ -51,9 +49,9 @@ class hierarchical_predecode(design.design):
debug.error("Invalid number of predecode inputs.",-1)
def setup_constraints(self):
# 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.m2_pitch = contact.m1m2.height + 2*self.m2_space
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)
# The rail offsets are indexed by the label
self.rails = {}
@@ -92,14 +90,14 @@ class hierarchical_predecode(design.design):
if label.startswith("in"):
self.add_layout_pin(text=label,
layer="metal2",
offset=vector(self.rails[label] - 0.5*self.m2_width, 0),
offset=vector(self.rails[label] - 0.5*self.m1_width, self.m1_width),
width=self.m2_width,
height=self.height - 2*self.m2_space)
height=self.height - 4*self.m1_width)
else:
self.add_rect(layer="metal2",
offset=vector(self.rails[label] - 0.5*self.m2_width, 0),
offset=vector(self.rails[label] - 0.5*self.m1_width, 2*self.m1_width),
width=self.m2_width,
height=self.height - 2*self.m2_space)
height=self.height - 4*self.m1_width)
def add_input_inverters(self):
""" Create the input inverters to invert input signals for the decode stage. """
@@ -205,11 +203,12 @@ class hierarchical_predecode(design.design):
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))
z_pin = self.inv_inst[num].get_pin("Z")
self.add_layout_pin(text="out[{}]".format(num),
layer="metal1",
offset=z_pin.ll(),
height=z_pin.height(),
width=z_pin.width())
def route_input_inverters(self):
@@ -268,25 +267,32 @@ class hierarchical_predecode(design.design):
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
# Find the x offsets for where the vias/pins should be placed
in_xoffset = self.in_inst[0].rx()
out_xoffset = self.inv_inst[0].lx()
for num in range(0,self.number_of_outputs):
# this will result in duplicate polygons for rails, but who cares
# use the inverter offset even though it will be the nand's too
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
# Route both supplies
for n in ["vdd", "gnd"]:
nand_pin = self.nand_inst[num].get_pin(n)
supply_offset = nand_pin.ll().scale(0,1)
self.add_rect(layer="metal1",
offset=supply_offset,
width=self.inv_inst[num].rx())
# route vdd
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.inv_inst[num].rx())
# route gnd
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.inv_inst[num].rx())
# Add pins in two locations
for xoffset in [in_xoffset, out_xoffset]:
pin_pos = vector(xoffset, nand_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos,
rotate=90)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
+12 -27
View File
@@ -71,14 +71,16 @@ class ms_flop_array(design.design):
for i in range(self.word_size):
for gnd_pin in self.ms_inst[i].get_pins("gnd"):
if gnd_pin.layer!="metal2":
continue
self.add_layout_pin(text="gnd",
layer="metal2",
offset=gnd_pin.ll(),
width=gnd_pin.width(),
height=gnd_pin.height())
# Route both supplies
for n in ["vdd", "gnd"]:
for supply_pin in self.ms_inst[i].get_pins(n):
pin_pos = supply_pin.center()
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
din_pins = self.ms_inst[i].get_pins("din")
for din_pin in din_pins:
@@ -110,26 +112,9 @@ class ms_flop_array(design.design):
width=self.width,
height=drc["minwidth_metal1"])
# Continous vdd rail along with label.
for vdd_pin in self.ms_inst[i].get_pins("vdd"):
if vdd_pin.layer!="metal1":
continue
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
width=self.width,
height=drc["minwidth_metal1"])
# Continous gnd rail along with label.
for gnd_pin in self.ms_inst[i].get_pins("gnd"):
if gnd_pin.layer!="metal1":
continue
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
width=self.width,
height=drc["minwidth_metal1"])
def analytical_delay(self, slew, load=0.0):
-201
View File
@@ -1,201 +0,0 @@
import contact
import pgate
import debug
from tech import drc, parameter
from ptx import ptx
from vector import vector
from globals import OPTS
class precharge(pgate.pgate):
"""
Creates a single precharge cell
This module implements the precharge bitline cell used in the design.
"""
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.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
self.beta = parameter["beta"]
self.ptx_width = self.beta*parameter["min_tx_size"]
self.width = self.bitcell.width
self.add_pins()
self.create_layout()
self.DRC_LVS()
def add_pins(self):
self.add_pin_list(["bl", "br", "en", "vdd"])
def create_layout(self):
self.create_ptx()
self.add_ptx()
self.connect_poly()
self.add_en()
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.pmos = ptx(width=self.ptx_width,
tx_type="pmos")
self.add_mod(self.pmos)
# 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_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(0, self.height - self.m1_width)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_position,
width=self.width,
height=self.m1_width)
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", "en", "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", "en", "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", "en", "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=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_en(self):
"""Adds the en input rail, en contact/vias, and connects to the pmos"""
# adds the en contact to connect the gates to the en 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 en rail on metal1
self.add_layout_pin_center_segment(text="en",
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_inst.uy() + contact.well.height/2 + drc["well_extend_active"])
self.add_contact_center(layers=("active", "contact", "metal1"),
offset=well_contact_pos,
implant_type="n",
well_type="n")
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 * self.m2_width,0)
self.add_layout_pin(text="bl",
layer="metal2",
offset=offset,
width=drc['minwidth_metal2'],
height=self.height)
# adds the BR on metal 2
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,
width=drc['minwidth_metal2'],
height=self.height)
def connect_to_bitlines(self):
self.add_bitline_contacts()
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"""
stack=("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)
def connect_pmos(self, pmos_pin, bit_pin):
""" Connect pmos pin to bitline pin """
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=ll_pos,
width=width,
height=height)
+15 -11
View File
@@ -37,28 +37,34 @@ class precharge_array(design.design):
def create_layout(self):
self.add_insts()
self.add_layout_pin(text="vdd",
layer="metal1",
offset=self.pc_cell.get_pin("vdd").ll(),
width=self.width,
height=drc["minwidth_metal1"])
self.add_layout_pins()
def add_layout_pins(self):
self.add_layout_pin(text="en",
layer="metal1",
offset=self.pc_cell.get_pin("en").ll(),
width=self.width,
height=drc["minwidth_metal1"])
for inst in self.local_insts:
self.copy_layout_pin(inst, "vdd")
def add_insts(self):
"""Creates a precharge array by horizontally tiling the precharge cell"""
self.local_insts = []
for i in range(self.columns):
name = "pre_column_{0}".format(i)
offset = vector(self.pc_cell.width * i, 0)
inst=self.add_inst(name=name,
mod=self.pc_cell,
offset=offset)
inst = self.add_inst(name=name,
mod=self.pc_cell,
offset=offset)
self.local_insts.append(inst)
self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "en", "vdd"])
bl_pin = inst.get_pin("bl")
self.add_layout_pin(text="bl[{0}]".format(i),
layer="metal2",
@@ -71,6 +77,4 @@ class precharge_array(design.design):
offset=br_pin.ll(),
width=drc["minwidth_metal2"],
height=bl_pin.height())
self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i),
"en", "vdd"])
+333 -166
View File
@@ -24,9 +24,6 @@ class replica_bitline(design.design):
g = reload(__import__(OPTS.replica_bitcell))
self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell)
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
for pin in ["en", "out", "vdd", "gnd"]:
self.add_pin(pin)
self.bitcell_loads = bitcell_loads
@@ -37,8 +34,15 @@ class replica_bitline(design.design):
self.calculate_module_offsets()
self.add_modules()
self.route()
self.offset_all_coordinates()
self.add_layout_pins()
self.add_lvs_correspondence_points()
#self.add_lvs_correspondence_points()
self.width = self.right_gnd_pin.rx() - self.left_gnd_pin.lx()
self.height = self.left_gnd_pin.uy() - self.left_gnd_pin.by()
self.DRC_LVS()
@@ -51,27 +55,22 @@ class replica_bitline(design.design):
# M1/M2 routing pitch is based on contacted pitch
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*(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.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
# Quadrant 1: Replica bitline and such are not rotated, but they must be placed far enough
# away from the delay chain/inverter with space for three M2 tracks
self.bitcell_offset = self.rbl_inv_offset + vector(2*self.m2_pitch, 0) + vector(0, self.bitcell.height + self.inv.width)
self.rbl_offset = self.bitcell_offset
self.bitcell_offset = vector(0,self.replica_bitcell.height)
self.rbl_offset = self.bitcell_offset
self.height = self.rbl_offset.y + self.rbl.height + self.m2_pitch
self.width = self.rbl_offset.x + self.bitcell.width
# Quadrant 4: with some space below it and tracks on the right for vdd/gnd
self.delay_chain_offset = vector(-self.delay_chain.width-4*self.m2_pitch,self.replica_bitcell.height)
# Will be flipped vertically below the delay chain
self.rbl_inv_offset = self.delay_chain_offset + vector(0.5*self.delay_chain.width, 0)
# Placed next to the replica bitcell
self.access_tx_offset = vector(-4*self.m2_pitch-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
def create_modules(self):
@@ -98,23 +97,20 @@ class replica_bitline(design.design):
# This is the threshold detect inverter on the output of the RBL
self.rbl_inv_inst=self.add_inst(name="rbl_inv",
mod=self.inv,
offset=self.rbl_inv_offset+vector(0,self.inv.width),
rotate=270,
mirror="MX")
offset=self.rbl_inv_offset,
rotate=180)
self.connect_inst(["bl[0]", "out", "vdd", "gnd"])
self.tx_inst=self.add_inst(name="rbl_access_tx",
mod=self.access_tx,
offset=self.access_tx_offset,
rotate=90)
offset=self.access_tx_offset)
# D, G, S, B
self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"])
# add the well and poly contact
self.dc_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain,
offset=self.delay_chain_offset,
rotate=90)
offset=self.delay_chain_offset)
self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
self.rbc_inst=self.add_inst(name="bitcell",
@@ -128,15 +124,31 @@ class replica_bitline(design.design):
offset=self.rbl_offset)
self.connect_inst(["bl[0]", "br[0]"] + ["gnd"]*self.bitcell_loads + ["vdd", "gnd"])
def route(self):
""" Connect all the signals together """
self.route_gnd()
self.route_vdd()
self.route_gnd()
self.route_access_tx()
def route_vdd_gnd(self):
""" Route all the vdd and gnd pins to the top level """
def route_vdd_gnd(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has
top_instances = [self.rbl_inst,
self.rbl_inv_inst,
self.rbc_inst,
self.dc_inst]
for inst in top_instances:
self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd")
def route_access_tx(self):
# GATE ROUTE
@@ -145,185 +157,340 @@ class replica_bitline(design.design):
# (middle in between the pmos and nmos)
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").bc().scale(1,0)
poly_offset = poly_pin.uc()
# This centers the contact above the poly by one pitch
contact_offset = poly_offset + vector(0,self.m2_pitch)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=contact_offset)
self.add_rect(layer="poly",
offset=poly_pin.lr(),
width=contact_offset.x-poly_offset.x,
height=self.poly_width)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=contact_offset)
self.add_segment_center(layer="poly",
start=poly_offset,
end=contact_offset)
nwell_offset = self.rbl_inv_offset + vector(-self.inv.height,self.inv.width)
self.add_rect(layer="nwell",
offset=nwell_offset,
width=0.5*self.inv.height,
height=self.delay_chain_offset.y-nwell_offset.y)
# self.add_rect(layer="nwell",
# offset=nwell_offset,
# width=0.5*self.inv.height,
# height=self.delay_chain_offset.y-nwell_offset.y)
# 2. Route delay chain output to access tx gate
delay_en_offset = self.dc_inst.get_pin("out").bc()
self.add_path("metal1", [delay_en_offset,contact_offset])
self.add_path("metal2", [delay_en_offset,contact_offset])
# 3. Route the mid-point of previous route to the bitcell WL
# 3. Route the contact 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(contact_offset.x,wl_offset.y)
self.add_path("metal1", [contact_offset, wl_mid, wl_offset])
xmid_point= 0.5*(wl_offset.x+contact_offset.x)
wl_mid1 = vector(xmid_point,contact_offset.y)
wl_mid2 = vector(xmid_point,wl_offset.y)
self.add_path("metal1", [contact_offset, wl_mid1, wl_mid2, wl_offset])
# DRAIN ROUTE
# 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])
drain_offset = self.tx_inst.get_pin("D").center()
self.add_path("metal1", [drain_offset, drain_offset.scale(1,0)])
# 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",[source_offset, mid1, mid2, inv_A_offset])
# 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,source_offset.y)
# Route the drain to the RBL inverter input
source_offset = self.tx_inst.get_pin("S").center()
inv_A_offset = self.rbl_inv_inst.get_pin("A").center()
self.add_path("metal1",[source_offset, inv_A_offset])
# Route the connection of the source route to the RBL bitline (left)
# 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*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])
self.add_path("metal3",[source_offset, bl_offset])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=source_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=source_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=bl_offset)
# BODY ROUTE
# Connect it to the inverter well
nwell_offset = self.rbl_inv_inst.lr()
ur_offset = self.tx_inst.ur()
self.add_rect(layer="nwell",
offset=nwell_offset,
width=ur_offset.x-nwell_offset.x,
height=ur_offset.y-nwell_offset.y)
def route_vdd(self):
# Add a rail in M2 that is to the right of the inverter gnd pin
# The replica column may not fit in a single standard cell pitch, so add the vdd rail to the
# right of it.
vdd_start = vector(self.bitcell_offset.x + self.bitcell.width + self.m1_pitch,0)
# It is the height of the entire RBL and bitcell
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_start,
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")
for pin in vdd_pins:
offset = vector(vdd_start.x,pin.by())
self.add_rect(layer="metal1",
offset=offset,
width=self.rbl_offset.x-vdd_start.x,
height=self.m1_width)
# Also connect the replica bitcell vdd pin to vdd
pin = self.rbc_inst.get_pin("vdd")
offset = vector(vdd_start.x,pin.by())
self.add_rect(layer="metal1",
offset=offset,
width=self.bitcell_offset.x-vdd_start.x,
height=self.m1_width)
""" Route all signals connected to vdd """
# Route the vdd lines from left to right
# Add via for the delay chain
left_vdd_start = self.dc_inst.ll().scale(1,0) - vector(self.m2_pitch,0)
left_vdd_end = vector(left_vdd_start.x, self.rbl_inst.uy())
self.left_vdd_pin=self.add_segment_center(layer="metal2",
start=left_vdd_start,
end=left_vdd_end)
# Vdd line to the left of the replica bitline
center_vdd_start = self.rbc_inst.ll() - vector(3*self.m2_pitch,0)
center_vdd_end = vector(center_vdd_start.x, self.rbl_inst.uy())
self.center_vdd_pin=self.add_segment_center(layer="metal2",
start=center_vdd_start,
end=center_vdd_end)
# Vdd line to the right of the replica bitline
right_vdd_start = self.rbc_inst.lr() + vector(2*self.m2_pitch,0)
right_vdd_end = vector(right_vdd_start.x, self.rbl_inst.uy())
self.right_vdd_pin=self.add_segment_center(layer="metal2",
start=right_vdd_start,
end=right_vdd_end)
# Connect the WL and vdd pins directly to the center and right vdd rails
# Connect RBL vdd pins to center and right rails
rbl_vdd_pins = self.rbl_inst.get_pins("vdd")
for pin in rbl_vdd_pins:
if pin.layer != "metal1":
continue
start = vector(self.center_vdd_pin.cx(),pin.cy())
end = vector(self.right_vdd_pin.cx(),pin.cy())
self.add_layout_pin_segment_center(text="vdd",
layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Connect the vdd pins of the delay chain to the left rails
dc_vdd_pins = self.dc_inst.get_pins("vdd")
for pin in dc_vdd_pins:
if pin.layer != "metal1":
continue
start = vector(self.left_vdd_pin.cx(),pin.cy())
# Note, we don't connect to center because of via conflicts
# with the RBL pins
#end = vector(center_vdd_pin.cx(),pin.cy())
end = pin.rc()
self.add_layout_pin_segment_center(text="vdd",
layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
# Add via for the inverter
pin = self.rbl_inv_inst.get_pin("vdd")
start = vector(self.left_vdd_pin.cx(),pin.cy())
end = vector(self.center_vdd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Add via for the RBC
pin = self.rbc_inst.get_pin("vdd")
start = pin.lc()
end = vector(self.right_vdd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Create the RBL rails too
rbl_pins = self.rbl_inst.get_pins("vdd")
for pin in rbl_pins:
if pin.layer != "metal1":
continue
# If above the delay line, route the full width
left = vector(self.left_vdd_pin.cx(),pin.cy())
center = vector(self.center_vdd_pin.cx(),pin.cy())
if pin.cy() > self.dc_inst.uy() + self.m1_pitch:
start = left
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left,
rotate=90)
else:
start = center
end = vector(self.right_vdd_pin.cx()+0.5*self.m1_width,pin.cy())
self.add_layout_pin_segment_center(text="vdd",
layer="metal1",
start=start,
end=end)
# 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=self.m1_width,
height=self.delay_chain_offset.y)
def route_gnd(self):
""" Route all signals connected to gnd """
gnd_start = self.rbl_inv_inst.get_pin("gnd").bc()
gnd_end = vector(gnd_start.x, self.rbl_inst.uy()+2*self.m2_pitch)
# Add a rail in M1 from bottom of delay chain to two above the RBL
# This prevents DRC errors with vias for the WL
dc_top = self.dc_inst.ur()
self.add_segment_center(layer="metal1",
start=vector(gnd_start.x, dc_top.y),
end=gnd_end)
# Add a rail in M2 from RBL inverter to two above the RBL
self.add_segment_center(layer="metal2",
start=gnd_start,
end=gnd_end)
# Add pin from bottom to RBL inverter
self.add_layout_pin_center_segment(text="gnd",
layer="metal1",
start=gnd_start.scale(1,0),
end=gnd_start)
# Route the gnd lines from left to right
# Add via for the delay chain
left_gnd_start = self.dc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
left_gnd_end = vector(left_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch)
self.left_gnd_pin=self.add_segment_center(layer="metal2",
start=left_gnd_start,
end=left_gnd_end)
# Gnd line to the left of the replica bitline
center_gnd_start = self.rbc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
center_gnd_end = vector(center_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch)
self.center_gnd_pin=self.add_segment_center(layer="metal2",
start=center_gnd_start,
end=center_gnd_end)
# Gnd line to the right of the replica bitline
right_gnd_start = self.rbc_inst.lr().scale(1,0) + vector(self.m2_pitch,0)
right_gnd_end = vector(right_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch)
self.right_gnd_pin=self.add_segment_center(layer="metal2",
start=right_gnd_start,
end=right_gnd_end)
# Connect the WL pins directly to gnd
gnd_pin = self.get_pin("gnd").rc()
# Connect the WL and gnd pins directly to the center and right gnd rails
for row in range(self.bitcell_loads):
wl = "wl[{}]".format(row)
pin = self.rbl_inst.get_pin(wl)
start = vector(gnd_pin.x,pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=pin.lc())
if pin.layer != "metal1":
continue
# If above the delay line, route the full width
left = vector(self.left_gnd_pin.cx(),pin.cy())
center = vector(self.center_gnd_pin.cx(),pin.cy())
if pin.cy() > self.dc_inst.uy() + self.m1_pitch:
start = left
else:
start = center
end = vector(self.right_gnd_pin.cx(),pin.cy())
self.add_layout_pin_segment_center(text="gnd",
layer="metal1",
start=start,
end=end)
if start == left:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start)
offset=center,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Add via for the delay chain
offset = self.dc_inst.get_pins("gnd")[0].bc() + vector(0.5*contact.m1m2.width,0.5*contact.m1m2.height)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=offset)
# Add via for the inverter
offset = self.rbl_inv_inst.get_pin("gnd").bc() - vector(0,0.5*contact.m1m2.height)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=offset)
# Connect the bitcell gnd pins to the rail
gnd_pins = self.get_pins("gnd")
gnd_start = gnd_pins[0].ul()
rbl_gnd_pins = self.rbl_inst.get_pins("gnd")
# Add L shapes to each vertical gnd rail
for pin in rbl_gnd_pins:
if pin.layer != "metal1":
continue
# If above the delay line, route the full width
left = vector(self.left_gnd_pin.cx(),pin.cy())
center = vector(self.center_gnd_pin.cx(),pin.cy())
if pin.cy() > self.dc_inst.uy() + self.m1_pitch:
start = left
else:
start = center
end = vector(self.right_gnd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
if start == left:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=center,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Connect the gnd pins of the delay chain to the left rails
dc_gnd_pins = self.dc_inst.get_pins("gnd")
for pin in dc_gnd_pins:
if pin.layer != "metal1":
continue
start = vector(self.left_gnd_pin.cx(),pin.cy())
# Note, we don't connect to the center rails because of
# via conflicts with the RBL
#end = vector(self.center_gnd_pin.cx(),pin.cy())
end = pin.rc()
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end,
#rotate=90)
# Add via for the inverter
# pin = self.rbl_inv_inst.get_pin("gnd")
# start = vector(self.left_gnd_pin.cx(),pin.cy())
# end = vector(self.center_gnd_pin.cx(),pin.cy())
# self.add_segment_center(layer="metal1",
# start=start,
# end=end)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=start,
#rotate=90)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end,
#rotate=90)
# Create RBL rails too
rbl_pins = self.rbl_inst.get_pins("gnd")
for pin in rbl_pins:
if pin.layer != "metal2":
continue
gnd_end = pin.uc()
gnd_mid = vector(gnd_end.x, gnd_start.y)
self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid, gnd_end])
gnd_start = gnd_mid
# Add a second gnd pin to the second delay chain rail. No need for full length.
dc_gnd_offset = self.dc_inst.get_pins("gnd")[1].ll()
self.add_layout_pin(text="gnd",
layer="metal1",
offset=dc_gnd_offset.scale(1,0),
width=self.m1_width,
height=self.delay_chain_offset.y)
start = vector(pin.cx(),self.right_gnd_pin.by())
end = vector(pin.cx(),self.right_gnd_pin.uy())
self.add_layout_pin_segment_center(text="gnd",
layer="metal2",
start=start,
end=end)
def add_layout_pins(self):
""" Route the input and output signal """
en_offset = self.dc_inst.get_pin("in").ll()
self.add_layout_pin(text="en",
layer="metal1",
offset=en_offset.scale(1,0),
width=self.m1_width,
height=en_offset.y)
en_offset = self.dc_inst.get_pin("in").bc()
self.add_layout_pin_segment_center(text="en",
layer="metal2",
start=en_offset,
end=en_offset.scale(1,0))
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=self.m1_width,
height=out_offset.y)
out_offset = self.rbl_inv_inst.get_pin("Z").center()
self.add_layout_pin_segment_center(text="out",
layer="metal2",
start=out_offset,
end=out_offset.scale(1,0))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
+22 -24
View File
@@ -62,51 +62,49 @@ class sense_amp_array(design.design):
br_offset = amp_position + br_pin.ll().scale(1,0)
dout_offset = amp_position + dout_pin.ll()
self.add_inst(name=name,
inst = self.add_inst(name=name,
mod=self.amp,
offset=amp_position)
self.connect_inst(["bl[{0}]".format(i),"br[{0}]".format(i),
self.connect_inst(["bl[{0}]".format(i),
"br[{0}]".format(i),
"data[{0}]".format(i/self.words_per_row),
"en", "vdd", "gnd"])
self.add_layout_pin(text="bl[{0}]".format(i),
gnd_pos = inst.get_pin("gnd").center()
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=gnd_pos)
self.add_layout_pin_rect_center(text="gnd",
layer="metal3",
offset=gnd_pos)
vdd_pos = inst.get_pin("vdd").center()
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=vdd_pos)
self.add_layout_pin_rect_center(text="vdd",
layer="metal3",
offset=vdd_pos)
self.add_layout_pin(text="bl[{0}]".format(i/self.words_per_row),
layer="metal2",
offset=bl_offset,
width=bl_pin.width(),
height=bl_pin.height())
self.add_layout_pin(text="br[{0}]".format(i),
self.add_layout_pin(text="br[{0}]".format(i/self.words_per_row),
layer="metal2",
offset=br_offset,
width=br_pin.width(),
height=br_pin.height())
self.add_layout_pin(text="data[{0}]".format(i/self.words_per_row),
layer="metal3",
layer="metal2",
offset=dout_offset,
width=dout_pin.width(),
height=dout_pin.height())
def connect_rails(self):
# add vdd rail across entire array
vdd_offset = self.amp.get_pin("vdd").ll().scale(0,1)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_offset,
width=self.width,
height=drc["minwidth_metal1"])
# NOTE:the gnd rails are vertical so it is not connected horizontally
# add gnd rail across entire array
gnd_offset = self.amp.get_pin("gnd").ll().scale(0,1)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_offset,
width=self.width,
height=drc["minwidth_metal1"])
# add sclk rail across entire array
sclk_offset = self.amp.get_pin("en").ll().scale(0,1)
self.add_layout_pin(text="en",
-173
View File
@@ -1,173 +0,0 @@
import design
import debug
from tech import drc, info
from vector import vector
import contact
from ptx import ptx
from globals import OPTS
class single_level_column_mux(design.design):
"""
This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell.
"""
def __init__(self, tx_size):
name="single_level_column_mux_{}".format(tx_size)
design.design.__init__(self, name)
debug.info(2, "create single column mux cell: {0}".format(name))
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
self.ptx_width = tx_size * drc["minwidth_tx"]
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
self.create_layout()
def create_layout(self):
self.add_ptx()
self.pin_height = 2*self.m2_width
self.width = self.bitcell.width
self.height = self.nmos2.uy() + self.pin_height
self.connect_poly()
self.add_gnd_rail()
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)
# bl and br
self.add_layout_pin(text="bl",
layer="metal2",
offset=bl_pos + vector(0,self.height - self.pin_height),
height=self.pin_height)
self.add_layout_pin(text="br",
layer="metal2",
offset=br_pos + vector(0,self.height - self.pin_height),
height=self.pin_height)
# bl_out and br_out
self.add_layout_pin(text="bl_out",
layer="metal2",
offset=bl_pos,
height=self.pin_height)
self.add_layout_pin(text="br_out",
layer="metal2",
offset=br_pos,
height=self.pin_height)
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)
# 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"])
# 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 """
height=self.nmos2.get_pin("G").uy() - self.nmos1.get_pin("G").by()
self.add_layout_pin(text="sel",
layer="poly",
offset=self.nmos1.get_pin("G").ll(),
height=height)
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")
# 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")
# 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
if gnd_pin.layer == "metal2" and gnd_pin.height >= self.bitcell.height:
gnd_position = vector(gnd_pin.lx(), 0)
self.add_layout_pin(text="gnd",
layer="metal2",
offset=gnd_position,
height=self.height)
def add_wells(self):
""" Add a well and implant over the whole cell. Also, add the pwell contact (if it exists) """
# find right most gnd rail
gnd_pins = self.bitcell.get_pins("gnd")
right_gnd = None
for gnd_pin in gnd_pins:
if right_gnd == None or gnd_pin.lx()>right_gnd.lx():
right_gnd = gnd_pin
# Add to the right (first) gnd rail
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,
implant_type="p",
well_type="p")
@@ -100,16 +100,9 @@ class single_level_column_mux_array(design.design):
offset=offset,
height=self.height-offset.y)
gnd_pins = mux_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
# only do even colums to avoid duplicates
offset = gnd_pin.ll()
if col_num % 2 == 0:
self.add_layout_pin(text="gnd",
layer="metal2",
offset=offset.scale(1,0),
height=self.height)
for inst in self.mux_inst:
self.copy_layout_pin(inst, "gnd")
def add_routing(self):
self.add_horizontal_input_rail()
@@ -119,7 +112,7 @@ class single_level_column_mux_array(design.design):
def add_horizontal_input_rail(self):
""" Create address input rails on M1 below the mux transistors """
for j in range(self.words_per_row):
offset = vector(0, self.route_height - (j+1)*self.m1_pitch)
offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch)
self.add_layout_pin(text="sel[{}]".format(j),
layer="metal1",
offset=offset,
+10 -14
View File
@@ -77,6 +77,16 @@ class tri_gate_array(design.design):
height=out_pin.height())
# Route both supplies
for n in ["vdd", "gnd"]:
for supply_pin in self.tri_inst[i].get_pins(n):
pin_pos = supply_pin.center()
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
width = self.tri.width * self.columns - (self.words_per_row - 1) * self.tri.width
en_pin = self.tri_inst[0].get_pin("en")
@@ -93,20 +103,6 @@ class tri_gate_array(design.design):
width=width,
height=drc["minwidth_metal1"])
vdd_pin = self.tri_inst[0].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0, 1),
width=width,
height=drc["minwidth_metal1"])
for gnd_pin in self.tri_inst[0].get_pins("gnd"):
if gnd_pin.layer=="metal1":
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0, 1),
width=width,
height=drc["minwidth_metal1"])
def analytical_delay(self, slew, load=0.0):
+97 -84
View File
@@ -22,6 +22,7 @@ class wordline_driver(design.design):
self.rows = rows
self.add_pins()
self.design_layout()
self.offset_all_coordinates()
self.DRC_LVS()
def add_pins(self):
@@ -36,11 +37,13 @@ class wordline_driver(design.design):
self.add_pin("gnd")
def design_layout(self):
self.add_layout()
self.offsets_of_gates()
self.create_layout()
self.create_modules()
self.add_modules()
self.route_layout()
self.route_vdd_gnd()
def add_layout(self):
def create_modules(self):
self.inv = pinv()
self.add_mod(self.inv)
@@ -50,18 +53,95 @@ class wordline_driver(design.design):
self.nand2 = pnand2()
self.add_mod(self.nand2)
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
# Find the x offsets for where the vias/pins should be placed
a_xoffset = self.inv1_inst[0].rx()
b_xoffset = self.inv2_inst[0].lx()
for num in range(self.rows):
# this will result in duplicate polygons for rails, but who cares
# use the inverter offset even though it will be the nand's too
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
# Route both supplies
for n in ["vdd", "gnd"]:
supply_pin = self.inv2_inst[num].get_pin(n)
# Add pins in two locations
for xoffset in [a_xoffset, b_xoffset]:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos,
rotate=90)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
def offsets_of_gates(self):
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
self.width = self.x_offset2 + self.inv.width
def add_modules(self):
inv1_xoffset = 2*self.m1_width + 5*self.m1_space
nand2_xoffset = inv1_xoffset + self.inv.width
inv2_xoffset = nand2_xoffset + self.nand2.width
self.width = inv2_xoffset + self.inv.width
self.height = self.inv.height * self.rows
def create_layout(self):
self.inv1_inst = []
self.nand_inst = []
self.inv2_inst = []
for row in range(self.rows):
name_inv1 = "wl_driver_inv_en{}".format(row)
name_nand = "wl_driver_nand{}".format(row)
name_inv2 = "wl_driver_inv{}".format(row)
if (row % 2):
y_offset = self.inv.height*(row + 1)
inst_mirror = "MX"
else:
y_offset = self.inv.height*row
inst_mirror = "R0"
inv1_offset = [inv1_xoffset, y_offset]
nand2_offset=[nand2_xoffset, y_offset]
inv2_offset=[inv2_xoffset, y_offset]
# add inv1 based on the info above
self.inv1_inst.append(self.add_inst(name=name_inv1,
mod=self.inv_no_output,
offset=inv1_offset,
mirror=inst_mirror))
self.connect_inst(["en",
"en_bar[{0}]".format(row),
"vdd", "gnd"])
# add nand 2
self.nand_inst.append(self.add_inst(name=name_nand,
mod=self.nand2,
offset=nand2_offset,
mirror=inst_mirror))
self.connect_inst(["en_bar[{0}]".format(row),
"in[{0}]".format(row),
"net[{0}]".format(row),
"vdd", "gnd"])
# add inv2
self.inv2_inst.append(self.add_inst(name=name_inv2,
mod=self.inv,
offset=inv2_offset,
mirror=inst_mirror))
self.connect_inst(["net[{0}]".format(row),
"wl[{0}]".format(row),
"vdd", "gnd"])
def route_layout(self):
""" Route all of the signals """
# Wordline enable connection
en_pin=self.add_layout_pin(text="en",
layer="metal2",
@@ -69,79 +149,12 @@ class wordline_driver(design.design):
width=self.m2_width,
height=self.height)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=[0, -0.5*self.m1_width],
width=self.x_offset0,
height=self.m1_width)
for row in range(self.rows):
name_inv1 = "wl_driver_inv_en{}".format(row)
name_nand = "wl_driver_nand{}".format(row)
name_inv2 = "wl_driver_inv{}".format(row)
inv_nand2B_connection_height = (abs(self.inv.get_pin("Z").ll().y
- self.nand2.get_pin("B").ll().y)
+ self.m1_width)
if (row % 2):
y_offset = self.inv.height*(row + 1)
inst_mirror = "MX"
cell_dir = vector(0,-1)
m1tm2_rotate=270
m1tm2_mirror="R0"
else:
y_offset = self.inv.height*row
inst_mirror = "R0"
cell_dir = vector(0,1)
m1tm2_rotate=90
m1tm2_mirror="MX"
name_inv1_offset = [self.x_offset0, y_offset]
nand2_offset=[self.x_offset1, y_offset]
inv2_offset=[self.x_offset2, y_offset]
base_offset = vector(self.width, y_offset)
# Extend vdd and gnd of wordline_driver
yoffset = (row + 1) * self.inv.height - 0.5 * self.m1_width
if (row % 2):
pin_name = "gnd"
else:
pin_name = "vdd"
self.add_layout_pin(text=pin_name,
layer="metal1",
offset=[0, yoffset],
width=self.x_offset0,
height=self.m1_width)
inv1_inst = self.inv1_inst[row]
nand_inst = self.nand_inst[row]
inv2_inst = self.inv2_inst[row]
# add inv1 based on the info above
inv1_inst=self.add_inst(name=name_inv1,
mod=self.inv_no_output,
offset=name_inv1_offset,
mirror=inst_mirror )
self.connect_inst(["en",
"en_bar[{0}]".format(row),
"vdd", "gnd"])
# add nand 2
nand_inst=self.add_inst(name=name_nand,
mod=self.nand2,
offset=nand2_offset,
mirror=inst_mirror)
self.connect_inst(["en_bar[{0}]".format(row),
"in[{0}]".format(row),
"net[{0}]".format(row),
"vdd", "gnd"])
# add inv2
inv2_inst=self.add_inst(name=name_inv2,
mod=self.inv,
offset=inv2_offset,
mirror=inst_mirror)
self.connect_inst(["net[{0}]".format(row),
"wl[{0}]".format(row),
"vdd", "gnd"])
# en connection
a_pin = inv1_inst.get_pin("A")
a_pos = a_pin.lc()
@@ -175,7 +188,7 @@ class wordline_driver(design.design):
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),
self.add_layout_pin_segment_center(text="in[{0}]".format(row),
layer="metal1",
start=input_offset,
end=mid_via_offset)
@@ -191,7 +204,7 @@ class wordline_driver(design.design):
# output each WL on the right
wl_offset = inv2_inst.get_pin("Z").rc()
self.add_layout_pin_center_segment(text="wl[{0}]".format(row),
self.add_layout_pin_segment_center(text="wl[{0}]".format(row),
layer="metal1",
start=wl_offset,
end=wl_offset-vector(self.m1_width,0))
+13 -12
View File
@@ -82,7 +82,19 @@ class write_driver_array(design.design):
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
for n in ["vdd", "gnd"]:
pin_list = self.driver_insts[i].get_pins(n)
for pin in pin_list:
pin_pos = pin.center()
# Add the M2->M3 stack
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
self.add_layout_pin(text="en",
layer="metal1",
@@ -90,16 +102,5 @@ class write_driver_array(design.design):
width=self.width,
height=drc['minwidth_metal1'])
self.add_layout_pin(text="vdd",
layer="metal1",
offset=self.driver_insts[0].get_pin("vdd").ll().scale(0,1),
width=self.width,
height=drc['minwidth_metal1'])
self.add_layout_pin(text="gnd",
layer="metal1",
offset=self.driver_insts[0].get_pin("gnd").ll().scale(0,1),
width=self.width,
height=drc['minwidth_metal1'])