mirror of
https://github.com/VLSIDA/OpenRAM.git
synced 2026-09-01 02:28:31 +02:00
Merging branch with PrivateRAM dev
This commit is contained in:
+354
-713
File diff suppressed because it is too large
Load Diff
@@ -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())
|
||||
@@ -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):
|
||||
|
||||
@@ -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"])
|
||||
|
||||
+421
-463
File diff suppressed because it is too large
Load Diff
+112
-123
@@ -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))
|
||||
|
||||
|
||||
|
||||
@@ -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"])
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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'])
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user