Reworking control logic for veritcal poly. Rewrote delay line. Rewrote buffered-DFF array.

This commit is contained in:
Matt Guthaus 2018-03-12 13:14:53 -07:00
parent c020d74f26
commit ed8eaed54f
16 changed files with 1184 additions and 777 deletions

View File

@ -544,6 +544,109 @@ class layout(lef.lef):
width=xmax-xmin, width=xmax-xmin,
height=ymax-ymin) height=ymax-ymin)
def add_power_ring(self, bbox):
"""
Create vdd and gnd power rings around an area of the bounding box argument. Must
have a supply_rail_width and supply_rail_pitch defined as a member variable.
Defines local variables of the left/right/top/bottom vdd/gnd center offsets
for use in other modules..
"""
[ll, ur] = bbox
supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width
height = (ur.y-ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing
width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
# LEFT vertical rails
offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch)
left_gnd_pin=self.add_layout_pin(text="gnd",
layer="metal2",
offset=offset,
width=self.supply_rail_width,
height=height)
offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch)
left_vdd_pin=self.add_layout_pin(text="vdd",
layer="metal2",
offset=offset,
width=self.supply_rail_width,
height=height)
# RIGHT vertical rails
offset = vector(ur.x,ll.y) + vector(0,-2*self.supply_rail_pitch)
right_gnd_pin = self.add_layout_pin(text="gnd",
layer="metal2",
offset=offset,
width=self.supply_rail_width,
height=height)
offset = vector(ur.x,ll.y) + vector(self.supply_rail_pitch,-1*self.supply_rail_pitch)
right_vdd_pin=self.add_layout_pin(text="vdd",
layer="metal2",
offset=offset,
width=self.supply_rail_width,
height=height)
# BOTTOM horizontal rails
offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch)
bottom_gnd_pin=self.add_layout_pin(text="gnd",
layer="metal1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch)
bottom_vdd_pin=self.add_layout_pin(text="vdd",
layer="metal1",
offset=offset,
width=width,
height=self.supply_rail_width)
# TOP horizontal rails
offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch,0)
top_gnd_pin=self.add_layout_pin(text="gnd",
layer="metal1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = vector(ll.x, ur.y) + vector(-1*self.supply_rail_pitch, self.supply_rail_pitch)
top_vdd_pin=self.add_layout_pin(text="vdd",
layer="metal1",
offset=offset,
width=width,
height=self.supply_rail_width)
# Remember these for connecting things in the design
self.left_gnd_x_center = left_gnd_pin.cx()
self.left_vdd_x_center = left_vdd_pin.cx()
self.right_gnd_x_center = right_gnd_pin.cx()
self.right_vdd_x_center = right_vdd_pin.cx()
self.bottom_gnd_y_center = bottom_gnd_pin.cy()
self.bottom_vdd_y_center = bottom_vdd_pin.cy()
self.top_gnd_y_center = top_gnd_pin.cy()
self.top_vdd_y_center = top_vdd_pin.cy()
via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center),
vector(self.left_gnd_x_center, self.top_gnd_y_center),
vector(self.right_gnd_x_center, self.bottom_gnd_y_center),
vector(self.right_gnd_x_center, self.top_gnd_y_center),
vector(self.left_vdd_x_center, self.bottom_vdd_y_center),
vector(self.left_vdd_x_center, self.top_vdd_y_center),
vector(self.right_vdd_x_center, self.bottom_vdd_y_center),
vector(self.right_vdd_x_center, self.top_vdd_y_center)]
for pt in via_points:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pt,
size = (3,3))
def pdf_write(self, pdf_name): def pdf_write(self, pdf_name):
# NOTE: Currently does not work (Needs further research) # NOTE: Currently does not work (Needs further research)
#self.pdf_name = self.name + ".pdf" #self.pdf_name = self.name + ".pdf"

View File

@ -182,10 +182,12 @@ class pin_layout:
width=self.width(), width=self.width(),
height=self.height(), height=self.height(),
center=False) center=False)
# Add the tet in the middle of the pin.
# This fixes some pin label offsetting when GDS gets imported into Magic.
newLayout.addText(text=self.name, newLayout.addText(text=self.name,
layerNumber=layer[self.layer], layerNumber=layer[self.layer],
purposeNumber=0, purposeNumber=0,
offsetInMicrons=self.ll(), offsetInMicrons=self.center(),
magnification=GDS["zoom"], magnification=GDS["zoom"],
rotate=None) rotate=None)

View File

@ -9,12 +9,15 @@ from pinv import pinv
from pnand2 import pnand2 from pnand2 import pnand2
from pnor2 import pnor2 from pnor2 import pnor2
from vector import vector from vector import vector
from pinvbuf import pinvbuf
from globals import OPTS from globals import OPTS
class bank(design.design): class bank(design.design):
""" """
Dynamically generated a single Bank including bitcell array, Dynamically generated a single bank including bitcell array,
hierarchical_decoder, precharge, column_mux, write driver and sense amplifiers. hierarchical_decoder, precharge, (optional column_mux and column decoder),
write driver and sense amplifiers.
""" """
def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""): def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""):
@ -40,7 +43,8 @@ class bank(design.design):
self.num_banks = num_banks self.num_banks = num_banks
# The local control signals are gated when we have bank select logic, # The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the input signals. # so this prefix will be added to all of the input signals to create
# the internal gated signals.
if self.num_banks>1: if self.num_banks>1:
self.prefix="gated_" self.prefix="gated_"
else: else:
@ -51,9 +55,10 @@ class bank(design.design):
self.create_modules() self.create_modules()
self.add_modules() self.add_modules()
self.setup_layout_constraints() self.setup_layout_constraints()
self.route_power_ring(self.core_bbox)
if self.num_banks > 1: self.add_power_ring(self.core_bbox)
# FIXME: Move this to the add modules function
self.add_bank_select() self.add_bank_select()
self.route_layout() self.route_layout()
@ -71,7 +76,7 @@ class bank(design.design):
for i in range(self.word_size): for i in range(self.word_size):
self.add_pin("DATA[{0}]".format(i)) self.add_pin("DATA[{0}]".format(i))
for i in range(self.addr_size): for i in range(self.addr_size):
self.add_pin("ADDR[{0}]".format(i)) self.add_pin("A[{0}]".format(i))
# For more than one bank, we have a bank select and name # For more than one bank, we have a bank select and name
# the signals gated_*. # the signals gated_*.
@ -83,14 +88,14 @@ class bank(design.design):
def route_layout(self): def route_layout(self):
""" Create routing amoung the modules """ """ Create routing amoung the modules """
self.create_central_bus() self.route_central_bus()
self.route_precharge_to_bitcell_array() self.route_precharge_to_bitcell_array()
self.route_sense_amp_to_trigate() self.route_sense_amp_to_trigate()
self.route_tri_gate_out() self.route_tri_gate_out()
self.route_wordline_driver() self.route_wordline_driver()
self.route_row_decoder() self.route_row_decoder()
self.route_address()
self.route_column_address_lines() self.route_column_address_lines()
self.route_msf_address()
self.route_control_lines() self.route_control_lines()
self.add_control_pins() self.add_control_pins()
if self.num_banks > 1: if self.num_banks > 1:
@ -101,9 +106,12 @@ class bank(design.design):
def add_modules(self): def add_modules(self):
""" Add modules. The order should not matter! """ """ Add modules. The order should not matter! """
# Above the bitcell array
self.add_bitcell_array() self.add_bitcell_array()
self.add_precharge_array() self.add_precharge_array()
# Below the bitcell array
if self.col_addr_size > 0: if self.col_addr_size > 0:
# The m2 width is because the 6T cell may have vias on the boundary edge for # The m2 width is because the 6T cell may have vias on the boundary edge for
# overlapping when making the array # overlapping when making the array
@ -111,16 +119,17 @@ class bank(design.design):
self.add_column_mux_array() self.add_column_mux_array()
else: else:
self.column_mux_height = 0 self.column_mux_height = 0
if self.col_addr_size > 1: # size 1 is from addr FF
self.add_column_decoder()
self.add_sense_amp_array() self.add_sense_amp_array()
self.add_write_driver_array() self.add_write_driver_array()
self.add_msf_data_in() self.add_msf_data_in()
self.add_tri_gate_array() self.add_tri_gate_array()
# To the left of the bitcell array
self.add_row_decoder() self.add_row_decoder()
self.add_wordline_driver() self.add_wordline_driver()
self.add_msf_address() self.add_column_decoder()
def compute_sizes(self): def compute_sizes(self):
""" Computes the required sizes to create the bank """ """ Computes the required sizes to create the bank """
@ -143,28 +152,29 @@ class bank(design.design):
self.num_control_lines = 6 self.num_control_lines = 6
# The order of the control signals on the control bus: # The order of the control signals on the control bus:
self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"] self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"]
# These will be outputs of the gaters if this is multibank # These will be outputs of the gaters if this is multibank, if not, normal signals.
if self.num_banks > 1: if self.num_banks > 1:
self.control_signals = ["gated_"+str for str in self.input_control_signals] self.control_signals = ["gated_"+str for str in self.input_control_signals]
else: else:
self.control_signals = self.input_control_signals self.control_signals = self.input_control_signals
# The central bus is the column address (both polarities), row address # The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0: if self.col_addr_size>0:
self.num_addr_lines = 2**self.col_addr_size + self.row_addr_size self.num_col_addr_lines = 2**self.col_addr_size
self.num_addr_lines = self.num_col_addr_lines + self.row_addr_size
else: else:
self.num_col_addr_lines = 0
self.num_addr_lines = self.row_addr_size self.num_addr_lines = self.row_addr_size
# M1/M2 routing pitch is based on contacted pitch # M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = contact.m1m2.height + max(self.m1_space,self.m2_space) 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.m2_pitch = contact.m2m3.height + max(self.m2_space,self.m3_space)
# Overall central bus gap. It includes all the column mux lines, # The width of this bus is needed to place other modules (e.g. decoder)
# control lines, and address flop to decoder lines self.central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 1)
# 1.5 pitches on the right on the right of the control lines for vias (e.g. column mux addr lines)
self.start_of_right_central_bus = -self.m2_pitch * (self.num_control_lines + 1.5) # The width of the bus below the decoder to route to the central bus
self.start_of_left_central_bus = self.start_of_right_central_bus - self.m2_pitch*(self.num_addr_lines) self.addr_bus_height = self.m1_pitch * (self.addr_size + 1)
# add a pitch on each side
self.overall_central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 3)
@ -196,13 +206,8 @@ class bank(design.design):
word_size=self.word_size) word_size=self.word_size)
self.add_mod(self.write_driver_array) self.add_mod(self.write_driver_array)
self.decoder = self.mod_decoder(rows=self.num_rows) self.row_decoder = self.mod_decoder(rows=self.num_rows)
self.add_mod(self.decoder) self.add_mod(self.row_decoder)
self.msf_address = self.mod_ms_flop_array(name="msf_address",
columns=self.row_addr_size+self.col_addr_size,
word_size=self.row_addr_size+self.col_addr_size)
self.add_mod(self.msf_address)
self.msf_data_in = self.mod_ms_flop_array(name="msf_data_in", self.msf_data_in = self.mod_ms_flop_array(name="msf_data_in",
columns=self.num_cols, columns=self.num_cols,
@ -245,7 +250,7 @@ class bank(design.design):
""" Adding Precharge """ """ Adding Precharge """
# The wells must be far enough apart # The wells must be far enough apart
# The enclosure is for the well and the spacig is to the bitcell wells # The enclosure is for the well and the spacing is to the bitcell wells
y_offset = self.bitcell_array.height + 2*drc["pwell_to_nwell"] + drc["well_enclosure_active"] y_offset = self.bitcell_array.height + 2*drc["pwell_to_nwell"] + drc["well_enclosure_active"]
self.precharge_array_inst=self.add_inst(name="precharge_array", self.precharge_array_inst=self.add_inst(name="precharge_array",
mod=self.precharge_array, mod=self.precharge_array,
@ -361,12 +366,11 @@ class bank(design.design):
# The predecoder is below the x-axis and the main decoder is above the x-axis # The predecoder is below the x-axis and the main decoder is above the x-axis
# The address flop and decoder are aligned in the x coord. # The address flop and decoder are aligned in the x coord.
decoder_x_offset = self.decoder.width + self.overall_central_bus_width decoder_x_offset = self.row_decoder.width + self.central_bus_width
addr_x_offset = self.msf_address.height offset = vector(decoder_x_offset,
offset = vector(max(decoder_x_offset, addr_x_offset), self.row_decoder.predecoder_height)
self.decoder.predecoder_height)
self.row_decoder_inst=self.add_inst(name="row_decoder", self.row_decoder_inst=self.add_inst(name="row_decoder",
mod=self.decoder, mod=self.row_decoder,
offset=offset.scale(-1,-1)) offset=offset.scale(-1,-1))
temp = [] temp = []
@ -383,7 +387,7 @@ class bank(design.design):
# The wordline driver is placed to the right of the main decoder width. # The wordline driver is placed to the right of the main decoder width.
# This means that it slightly overlaps with the hierarchical decoder, # This means that it slightly overlaps with the hierarchical decoder,
# but it shares power rails. This may differ for other decoders later... # but it shares power rails. This may differ for other decoders later...
x_offset = self.decoder.width + self.overall_central_bus_width - self.decoder.row_decoder_width x_offset = self.row_decoder.width + self.central_bus_width - self.row_decoder.row_decoder_width
self.wordline_driver_inst=self.add_inst(name="wordline_driver", self.wordline_driver_inst=self.add_inst(name="wordline_driver",
mod=self.wordline_driver, mod=self.wordline_driver,
offset=vector(x_offset,0).scale(-1,-1)) offset=vector(x_offset,0).scale(-1,-1))
@ -398,73 +402,54 @@ class bank(design.design):
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def add_msf_address(self):
""" Adding address wires """
# A gap between the hierarchical decoder and addr flops def add_column_decoder_module(self):
gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch) """
Create a 2:4 or 3:8 column address decoder.
# The address flops go below the hierarchical decoder """
decoder_x_offset = self.decoder.width + self.overall_central_bus_width # Place the col decoder aligned left to row decoder
addr_x_offset = self.msf_address.height + self.overall_central_bus_width x_off = -(self.central_bus_width + self.row_decoder.width)
# msf_address is not in the y-coord because it is rotated y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + self.addr_bus_height)
offset = vector(max(decoder_x_offset, addr_x_offset),
self.decoder.predecoder_height + gap)
self.msf_address_inst=self.add_inst(name="address_flop_array",
mod=self.msf_address,
offset=offset.scale(-1,-1),
rotate=270)
temp = []
for i in range(self.row_addr_size+self.col_addr_size):
temp.append("ADDR[{0}]".format(i))
for i in range(self.row_addr_size+self.col_addr_size):
if self.col_addr_size==1 and i==self.row_addr_size:
temp.extend(["sel[1]","sel[0]"])
else:
temp.extend(["A[{0}]".format(i),"A_bar[{0}]".format(i)])
temp.append(self.prefix+"clk_buf")
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def add_column_decoder(self):
""" Create a 2:4 decoder to decode column select lines if the col_addr_size = 4 """
if self.col_addr_size == 1:
return # This is done from the FF outputs directly
if self.col_addr_size == 2:
# FIXME: Should just load this rather than reference a level down
self.col_decoder = self.decoder.pre2_4
elif self.col_addr_size == 3:
debug.error("8 way column mux not yet supported...", -1)
# FIXME: Should just load this rather than reference a level down
self.col_decoder = self.decoder.pre3_8
else:
# No error checking before?
debug.error("Invalid column decoder?",-1)
# Place the col decoder just to the left of the control bus
gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch)
x_off = gap + self.overall_central_bus_width + self.col_decoder.width
# Place the col decoder below the the address flops which are below the row decoder (lave some space for wells)
vertical_gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch)
y_off = self.decoder.predecoder_height + self.msf_address.width + self.col_decoder.height + 2*vertical_gap
self.col_decoder_inst=self.add_inst(name="col_address_decoder", self.col_decoder_inst=self.add_inst(name="col_address_decoder",
mod=self.col_decoder, mod=self.col_decoder,
offset=vector(x_off,y_off).scale(-1,-1)) offset=vector(x_off,y_off))
temp = [] temp = []
for i in range(self.col_addr_size): for i in range(self.col_addr_size):
temp.append("A[{0}]".format(i + self.row_addr_size)) temp.append("A[{0}]".format(i + self.row_addr_size))
for j in range(2**self.col_addr_size): for j in range(self.num_col_addr_lines):
temp.append("sel[{0}]".format(j)) temp.append("sel[{0}]".format(j))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def add_column_decoder(self):
"""
Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2,
2:4 decoder, or 3:8 decoder.
"""
if self.col_addr_size == 0:
return
elif self.col_addr_size == 1:
self.col_decoder = pinvbuf()
self.add_mod(self.col_decoder)
elif self.col_addr_size == 2:
self.col_decoder = self.row_decoder.pre2_4
elif self.col_addr_size == 3:
self.col_decoder = self.row_decoder.pre3_8
else:
# No error checking before?
debug.error("Invalid column decoder?",-1)
self.add_column_decoder_module()
def add_bank_select(self): def add_bank_select(self):
""" Instantiate the bank select logic. """ """ Instantiate the bank select logic. """
xoffset = self.left_vdd_x_offset + self.supply_rail_pitch
if not self.num_banks > 1:
return
xoffset = -(self.central_bus_width + self.bank_select.width)
# extra space to allow vias # extra space to allow vias
yoffset = self.min_point + 2*self.supply_rail_pitch + self.m1_space yoffset = self.min_point + 2*self.supply_rail_pitch + self.m1_space
self.bank_select_pos = vector(xoffset,yoffset) self.bank_select_pos = vector(xoffset,yoffset)
@ -492,7 +477,7 @@ class bank(design.design):
for gated_name in self.control_signals: for gated_name in self.control_signals:
# Connect the inverter output to the central bus # Connect the inverter output to the central bus
out_pos = self.bank_select_inst.get_pin(gated_name).rc() out_pos = self.bank_select_inst.get_pin(gated_name).rc()
bus_pos = vector(self.central_line_xoffset[gated_name], out_pos.y) bus_pos = vector(self.bus_xoffset[gated_name], out_pos.y)
self.add_path("metal3",[out_pos, bus_pos]) self.add_path("metal3",[out_pos, bus_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=bus_pos, offset=bus_pos,
@ -506,34 +491,36 @@ class bank(design.design):
def setup_layout_constraints(self): def setup_layout_constraints(self):
""" Calculating layout constraints, width, height etc """ """ After the modules are instantiated, find the dimensions for the
control bus, power ring, etc. """
#The minimum point is either the bottom of the address flops, #The minimum point is either the bottom of the address flops,
#the column decoder (if there is one) or the tristate output #the column decoder (if there is one) or the tristate output
#driver. #driver.
# Leave room for the output below the tri gate. # Leave room for the output below the tri gate.
tri_gate_min_point = self.tri_gate_array_inst.ll().y - 3*self.m2_pitch tri_gate_min_point = self.tri_gate_array_inst.by() - 3*self.m2_pitch
addr_min_point = self.msf_address_inst.ll().y - 2*self.m2_pitch row_decoder_min_point = self.row_decoder_inst.by()
if self.col_addr_size > 0:
if self.col_addr_size >1: col_decoder_min_point = self.col_decoder_inst.by()
self.decoder_min_point = self.col_decoder_inst.ll().y
else: else:
self.decoder_min_point = addr_min_point col_decoder_min_point = row_decoder_min_point - self.addr_bus_height
self.addr_min_point = row_decoder_min_point - self.addr_bus_height
if self.num_banks>1: if self.num_banks>1:
# The control gating logic is below the decoder # The control gating logic is below the decoder
# Min of the control gating logic and tri gate. # Min of the control gating logic and tri gate.
self.min_point = min(self.decoder_min_point - self.bank_select.height, tri_gate_min_point) self.min_point = min(col_decoder_min_point - self.bank_select.height, tri_gate_min_point)
else: else:
# Just the min of the decoder logic logic and tri gate. # Just the min of the decoder logic logic and tri gate.
self.min_point = min(self.decoder_min_point, addr_min_point, tri_gate_min_point) self.min_point = min(col_decoder_min_point, tri_gate_min_point)
# The max point is always the top of the precharge bitlines # The max point is always the top of the precharge bitlines
# Add a vdd and gnd power rail above the array # Add a vdd and gnd power rail above the array
self.max_point = self.precharge_array_inst.uy() + 3*self.m1_width self.max_point = self.precharge_array_inst.uy() + 3*self.m1_width
# Create the core bbox for the power rings # Create the core bbox for the power rings
ur = vector(self.bitcell_array_inst.ur().x + 3*self.m1_width, self.max_point) ur = vector(self.bitcell_array_inst.ur().x + 3*self.m1_width, self.max_point)
ll = vector(min(self.msf_address_inst.ll().x, self.row_decoder_inst.ll().x), self.min_point) ll = vector(self.row_decoder_inst.lx(), self.min_point)
self.core_bbox = [ll, ur] self.core_bbox = [ll, ur]
# Compute the vertical rail positions for later use # Compute the vertical rail positions for later use
@ -550,145 +537,62 @@ class bank(design.design):
self.height = self.max_point - self.min_point self.height = self.max_point - self.min_point
self.width = self.right_vdd_x_offset - self.left_gnd_x_offset + self.supply_rail_width self.width = self.right_vdd_x_offset - self.left_gnd_x_offset + self.supply_rail_width
def route_power_ring(self, bbox):
""" Create vdd and gnd power rings around an area of the bounding box argument.. """
[ll, ur] = bbox
# LEFT vertical rails
offset = ll.scale(1,0) + vector(-2*self.supply_rail_pitch, self.min_point)
left_gnd_pin=self.add_layout_pin(text="gnd",
layer="metal2",
offset=offset,
width=self.supply_rail_width,
height=self.height)
offset = ll.scale(1,0) + vector(-1*self.supply_rail_pitch, self.min_point)
left_vdd_pin=self.add_layout_pin(text="vdd",
layer="metal2",
offset=offset,
width=self.supply_rail_width,
height=self.height)
# RIGHT vertical rails
offset = ur.scale(1,0) + vector(0,self.min_point)
right_gnd_pin = self.add_layout_pin(text="gnd",
layer="metal2",
offset=offset,
width=self.supply_rail_width,
height=self.height)
offset = ur.scale(1,0) + vector(self.supply_rail_pitch,self.min_point)
right_vdd_pin=self.add_layout_pin(text="vdd",
layer="metal2",
offset=offset,
width=self.supply_rail_width,
height=self.height)
# BOTTOM horizontal rails
offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch)
bottom_gnd_pin=self.add_layout_pin(text="gnd",
layer="metal1",
offset=offset,
width=self.width,
height=self.supply_rail_width)
offset = ll + vector(-2*self.supply_rail_pitch, -1*self.supply_rail_pitch)
bottom_vdd_pin=self.add_layout_pin(text="vdd",
layer="metal1",
offset=offset,
width=self.width,
height=self.supply_rail_width)
# TOP horizontal rails
offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch,0)
top_gnd_pin=self.add_layout_pin(text="gnd",
layer="metal1",
offset=offset,
width=self.width,
height=self.supply_rail_width)
offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch, self.supply_rail_pitch)
top_vdd_pin=self.add_layout_pin(text="vdd",
layer="metal1",
offset=offset,
width=self.width,
height=self.supply_rail_width)
# Remember these for connecting things in the design
self.left_gnd_x_center = left_gnd_pin.cx()
self.left_vdd_x_center = left_vdd_pin.cx()
self.right_gnd_x_center = right_gnd_pin.cx()
self.right_vdd_x_center = right_vdd_pin.cx()
self.bottom_gnd_y_center = bottom_gnd_pin.cy()
self.bottom_vdd_y_center = bottom_vdd_pin.cy()
self.top_gnd_y_center = top_gnd_pin.cy()
self.top_vdd_y_center = top_vdd_pin.cy()
via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center),
vector(self.left_gnd_x_center, self.top_gnd_y_center),
vector(self.right_gnd_x_center, self.bottom_gnd_y_center),
vector(self.right_gnd_x_center, self.top_gnd_y_center),
vector(self.left_vdd_x_center, self.bottom_vdd_y_center),
vector(self.left_vdd_x_center, self.top_vdd_y_center),
vector(self.right_vdd_x_center, self.bottom_vdd_y_center),
vector(self.right_vdd_x_center, self.top_vdd_y_center)]
for pt in via_points:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pt,
size = (3,3))
def route_central_bus(self):
def create_central_bus(self):
""" Create the address, supply, and control signal central bus lines. """ """ Create the address, supply, and control signal central bus lines. """
# Address lines in central line connection are 2*col_addr_size # Overall central bus width. It includes all the column mux lines,
# number of connections for the column mux (for both signal and _bar) and row_addr_size (no _bar) # control lines, and address flop to decoder lines.
# The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_x_offset = -self.m2_pitch * (self.num_control_lines)
address_bus_x_offset = control_bus_x_offset - self.m2_pitch*(self.num_addr_lines)
self.central_line_xoffset = {} # Track the bus offsets for other modules to access
self.bus_xoffset = {}
# Control lines (to the right of the GND rail) # Control lines
for i in range(self.num_control_lines): for i in range(self.num_control_lines):
x_offset = self.start_of_right_central_bus + i*self.m2_pitch x_offset = control_bus_x_offset + i*self.m2_pitch
self.central_line_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width # Make the xoffset map the center of the rail
self.bus_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width
# Pins are added later if this is a single bank, so just add rectangle now # Pins are added later if this is a single bank, so just add rectangle now
self.add_rect(layer="metal2", self.add_rect(layer="metal2",
offset=vector(x_offset, self.min_point), offset=vector(x_offset, self.min_point),
width=self.m2_width, width=self.m2_width,
height=self.height) height=self.height)
# row address lines (to the left of the column mux or GND rail) # Row address lines (to left of col address lines)
# goes from 0 down to the bottom of the address flops # goes from bottom of bitcell array down to the bottom of the column decoder/addresses
for i in range(self.row_addr_size): for i in range(self.row_addr_size):
x_offset = self.start_of_left_central_bus + i*self.m2_pitch addr_idx = i + self.col_addr_size
name = "A[{}]".format(i) x_offset = address_bus_x_offset + i*self.m2_pitch
self.central_line_xoffset[name]=x_offset + 0.5*self.m2_width name = "A[{}]".format(addr_idx)
# Make the xoffset map the center of the rail
self.bus_xoffset[name]=x_offset + 0.5*self.m2_width
# Add a label pin for LVS correspondence and visual help inspecting the rail. # Add a label pin for LVS correspondence and visual help inspecting the rail.
self.add_label_pin(text=name, self.add_label_pin(text=name,
layer="metal2", layer="metal2",
offset=vector(x_offset, self.decoder_min_point), offset=vector(x_offset, self.addr_min_point),
width=self.m2_width, width=self.m2_width,
height=-self.decoder_min_point) height=-self.addr_min_point)
# column mux lines if there is column mux [2 or 4 lines] (to the left of the GND rail) # Column mux lines if there is column mux
# goes from 0 down to the min point # goes from bottom of bitcell array down to the bottom of the column decoder/addresses
if self.col_addr_size>0: if self.col_addr_size>0:
for i in range(2**self.col_addr_size): for i in range(self.num_col_addr_lines):
x_offset = self.start_of_left_central_bus + (i+self.row_addr_size)*self.m2_pitch x_offset = address_bus_x_offset + (i+self.row_addr_size)*self.m2_pitch
name = "sel[{}]".format(i) name = "sel[{}]".format(i) # One hot select signals
self.central_line_xoffset[name]=x_offset + 0.5*self.m2_width # Make the xoffset map the center of the rail
self.bus_xoffset[name]=x_offset + 0.5*self.m2_width
# Add a label pin for LVS correspondence # Add a label pin for LVS correspondence
self.add_label_pin(text=name, self.add_label_pin(text=name,
layer="metal2", layer="metal2",
offset=vector(x_offset, self.decoder_min_point), offset=vector(x_offset, self.col_decoder_inst.by()),
width=self.m2_width, width=self.m2_width,
height=-self.decoder_min_point) height=-self.col_decoder_inst.by())
def route_precharge_to_bitcell_array(self): def route_precharge_to_bitcell_array(self):
@ -758,10 +662,10 @@ class bank(design.design):
def route_row_decoder(self): def route_row_decoder(self):
""" Routes the row decoder inputs and supplies """ """ Routes the row decoder inputs and supplies """
for i in range(self.row_addr_size): for i in range(self.row_addr_size):
addr_idx = i + self.col_addr_size
# before this index, we are using 2x4 decoders # before this index, we are using 2x4 decoders
switchover_index = 2*self.decoder.no_of_pre2x4 switchover_index = 2*self.row_decoder.no_of_pre2x4
# so decide what modulus to perform the height spacing # so decide what modulus to perform the height spacing
if i < switchover_index: if i < switchover_index:
position_heights = i % 2 position_heights = i % 2
@ -770,8 +674,10 @@ class bank(design.design):
# Connect the address rails to the decoder # Connect the address rails to the decoder
# Note that the decoder inputs are long vertical rails so spread out the connections vertically. # Note that the decoder inputs are long vertical rails so spread out the connections vertically.
decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() + vector(0,position_heights*self.bitcell.height+self.m2_pitch) decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() \
rail_position = vector(self.central_line_xoffset["A[{}]".format(i)],decoder_in_position.y) + vector(0,position_heights*self.bitcell.height+self.m2_pitch)
rail_position = vector(self.bus_xoffset["A[{}]".format(addr_idx)],
decoder_in_position.y)
self.add_path("metal1",[decoder_in_position,rail_position]) self.add_path("metal1",[decoder_in_position,rail_position])
decoder_in_via = decoder_in_position - vector(0,0.5*self.m2_width) decoder_in_via = decoder_in_position - vector(0,0.5*self.m2_width)
@ -841,35 +747,64 @@ class bank(design.design):
# Connect the select lines to the column mux # Connect the select lines to the column mux
# These must be in metal3 so that they don't overlap any gnd lines from decoders # These must be in metal3 so that they don't overlap any gnd lines from decoders
for i in range(2**self.col_addr_size): for i in range(self.num_col_addr_lines):
name = "sel[{}]".format(i) name = "sel[{}]".format(i)
mux_addr_pos = self.col_mux_array_inst.get_pin(name).lc() mux_addr_pos = self.col_mux_array_inst.get_pin(name).lc()
wire_pos = vector(self.central_line_xoffset[name], mux_addr_pos.y) wire_pos = vector(self.bus_xoffset[name], mux_addr_pos.y)
self.add_path("metal1", [wire_pos,mux_addr_pos]) self.add_path("metal1", [wire_pos,mux_addr_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=wire_pos, offset=wire_pos,
rotate=90) rotate=90)
# Take care of the column address decoder routing if self.col_addr_size == 1:
# If there is a 2:4 decoder for column select lines
# or TODO 3:8 decoder should work too!
if self.col_addr_size > 1:
# connections between outputs of decoder to the extension of decode_out_pos = self.col_decoder_inst.get_pin("Zb").rc()
# main address bus selx_pos = vector(self.bus_xoffset["sel[0]"],decode_out_pos.y)
for i in range(2**self.col_addr_size): self.add_path("metal1",[decode_out_pos, selx_pos])
name = "sel[{}]".format(i) self.add_via_center(layers=("metal1", "via1", "metal2"),
x_offset = self.central_line_xoffset[name] offset=selx_pos,
decode_out_pos = self.col_decoder_inst.get_pin("out[{}]".format(i)).rc() rotate=90)
selx_pos = vector(self.central_line_xoffset[name],decode_out_pos.y) decode_out_pos = self.col_decoder_inst.get_pin("Z").rc()
selx_pos = vector(self.bus_xoffset["sel[1]"],decode_out_pos.y)
self.add_path("metal1",[decode_out_pos, selx_pos]) self.add_path("metal1",[decode_out_pos, selx_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=selx_pos, offset=selx_pos,
rotate=90) rotate=90)
# The Address LSB
decode_in_pin = self.col_decoder_inst.get_pin("A")
pin_pos = vector(self.left_gnd_x_offset, decode_in_pin.cy())
self.add_layout_pin_center_segment(text="A[0]",
layer="metal1",
start=pin_pos,
end=decode_in_pin.lc())
elif self.col_addr_size > 1:
# Route the col decoder outputs to the col select bus
for i in range(self.num_col_addr_lines):
name = "sel[{}]".format(i)
decode_out_pos = self.col_decoder_inst.get_pin("out[{}]".format(i)).rc()
selx_pos = vector(self.bus_xoffset[name],decode_out_pos.y)
self.add_path("metal1",[decode_out_pos, selx_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=selx_pos,
rotate=90)
# Route from the col decoder up to the address bus
for i in range(self.col_addr_size):
name = "A[{}]".format(i)
decode_in_pin = self.col_decoder_inst.get_pin("in[{}]".format(i))
addr_pin = self.get_pin(name)
addr_pos = vector(decode_in_pin.cx(), addr_pin.by() + 0.5*self.m1_width)
self.add_path("metal2", [addr_pos, decode_in_pin.uc()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=addr_pos,
rotate=90)
# route the gnd rails, add contact to rail as well # route the gnd rails, add contact to rail as well
for gnd_pin in self.col_decoder_inst.get_pins("gnd"): for gnd_pin in self.col_decoder_inst.get_pins("gnd"):
left_rail_pos = vector(self.left_gnd_x_offset, gnd_pin.cy()) left_rail_pos = vector(self.left_gnd_x_center, gnd_pin.cy())
self.add_path("metal1", [left_rail_pos, gnd_pin.rc()]) self.add_path("metal1", [left_rail_pos, gnd_pin.rc()])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left_rail_pos, offset=left_rail_pos,
@ -885,112 +820,28 @@ class bank(design.design):
size = (1,3), size = (1,3),
rotate=90) rotate=90)
# The connection between last address flops to the input
# of the column_mux line decoder
for i in range(self.col_addr_size):
ff_index = i + self.row_addr_size
dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(ff_index)).rc()
in_pos = self.col_decoder_inst.get_pin("in[{}]".format(i)).uc()
mid_pos = vector(in_pos.x,dout_pos.y)
self.add_path("metal3",[dout_pos, mid_pos, in_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"), def route_address(self):
offset=dout_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=in_pos)
# if there are only two column select lines we just connect the dout_bar of the last FF
# to only select line and dout of that FF to the other select line
elif self.col_addr_size == 1:
dout_bar_pos = self.msf_address_inst.get_pin("dout_bar[{}]".format(self.row_addr_size)).rc()
sel0_pos = vector(self.central_line_xoffset["sel[0]"],dout_bar_pos.y)
self.add_path("metal1",[dout_bar_pos, sel0_pos])
# two vias on both ends
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=dout_bar_pos,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=sel0_pos,
rotate=90)
dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(self.row_addr_size)).rc()
sel1_pos = vector(self.central_line_xoffset["sel[1]"],dout_pos.y)
self.add_path("metal1",[dout_pos, sel1_pos])
# two vias on both ends
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=dout_pos,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=sel1_pos,
rotate=90)
def route_msf_address(self):
""" Routing the row address lines from the address ms-flop array to the row-decoder """ """ Routing the row address lines from the address ms-flop array to the row-decoder """
# Create the address input pins
for i in range(self.addr_size): for i in range(self.addr_size):
msf_din_pins = self.msf_address_inst.get_pins("din[{}]".format(i)) name = "A[{}]".format(i)
for pin in msf_din_pins: address_y_offset = self.addr_min_point + (i+1)*self.m1_pitch
if pin.layer=="metal3": pin_pos = vector(self.left_gnd_x_offset, address_y_offset)
msf_din_pos=pin.ll() if name in self.bus_xoffset:
break rail_pos = vector(self.bus_xoffset[name],pin_pos.y)
address_pos = vector(self.left_gnd_x_offset, msf_din_pos.y) else:
self.add_layout_pin(text="ADDR[{}]".format(i), # Route to right of col decoder if it's not part of central bus
layer="metal3", rail_pos = vector(self.col_decoder_inst.rx(),pin_pos.y)
offset=address_pos, self.add_layout_pin_center_segment(text=name,
width=msf_din_pos.x - address_pos.x) layer="metal1",
start=pin_pos,
end=rail_pos)
for i in range(self.row_addr_size):
# Connect the ff outputs to the rails
dout_pos = self.msf_address_inst.get_pin("dout[{}]".format(i)).rc()
rail_pos = vector(self.central_line_xoffset["A[{}]".format(i)],dout_pos.y)
self.add_path("metal1",[dout_pos, rail_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=dout_pos,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_pos, offset=rail_pos,
rotate=90) rotate=90)
# Connect address FF gnd
for gnd_pin in self.msf_address_inst.get_pins("gnd"):
if gnd_pin.layer != "metal2":
continue
gnd_via = gnd_pin.ll() + vector(contact.m1m2.height,0)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=gnd_via,
rotate=90)
gnd_offset = gnd_pin.lc()
rail_offset = vector(self.left_gnd_x_center, gnd_offset.y)
self.add_path("metal1",[gnd_offset,rail_offset])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_offset,
size = (1,3),
rotate=90)
# Connect address FF vdd
for vdd_pin in self.msf_address_inst.get_pins("vdd"):
if vdd_pin.layer != "metal1":
continue
vdd_offset = vdd_pin.bc()
mid = vector(vdd_offset.x, vdd_offset.y - self.m1_pitch)
rail_offset = vector(self.left_vdd_x_center, mid.y)
self.add_path("metal1", [vdd_offset,mid,rail_offset])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=rail_offset,
size = (1,3),
rotate=90)
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong. """ This adds some points for easier debugging if LVS goes wrong.
@ -1051,27 +902,17 @@ class bank(design.design):
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc())) connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
for (control_signal, pin_pos) in connection: for (control_signal, pin_pos) in connection:
control_pos = vector(self.central_line_xoffset[control_signal], pin_pos.y) control_pos = vector(self.bus_xoffset[control_signal], pin_pos.y)
self.add_path("metal1", [control_pos, pin_pos]) self.add_path("metal1", [control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos, offset=control_pos,
rotate=90) rotate=90)
# clk to msf address
control_signal = self.prefix+"clk_buf"
pin_pos = self.msf_address_inst.get_pin("clk").uc()
mid_pos = pin_pos + vector(0,self.m1_pitch)
control_pos = vector(self.central_line_xoffset[control_signal], mid_pos.y)
self.add_path("metal1",[pin_pos, mid_pos, control_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos,
rotate=90)
# clk to wordline_driver # clk to wordline_driver
control_signal = self.prefix+"clk_buf" control_signal = self.prefix+"clk_buf"
pin_pos = self.wordline_driver_inst.get_pin("en").uc() pin_pos = self.wordline_driver_inst.get_pin("en").uc()
mid_pos = pin_pos + vector(0,self.m1_pitch) mid_pos = pin_pos + vector(0,self.m1_pitch)
control_x_offset = self.central_line_xoffset[control_signal] control_x_offset = self.bus_xoffset[control_signal]
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y) control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
control_via_pos = vector(control_x_offset, mid_pos.y) control_via_pos = vector(control_x_offset, mid_pos.y)
@ -1162,18 +1003,19 @@ class bank(design.design):
""" Add the control signal input pins """ """ Add the control signal input pins """
for ctrl in self.control_signals: for ctrl in self.control_signals:
x_offset = self.central_line_xoffset[ctrl] # xoffsets are the center of the rail
x_offset = self.bus_xoffset[ctrl] - 0.5*self.m2_width
if self.num_banks > 1: if self.num_banks > 1:
# it's not an input pin if we have multiple banks # it's not an input pin if we have multiple banks
self.add_label_pin(text=ctrl, self.add_label_pin(text=ctrl,
layer="metal2", layer="metal2",
offset=vector(x_offset - 0.5*self.m2_width, self.min_point), offset=vector(x_offset, self.min_point),
width=self.m2_width, width=self.m2_width,
height=self.height) height=self.height)
else: else:
self.add_layout_pin(text=ctrl, self.add_layout_pin(text=ctrl,
layer="metal2", layer="metal2",
offset=vector(x_offset - 0.5*self.m2_width, self.min_point), offset=vector(x_offset, self.min_point),
width=self.m2_width, width=self.m2_width,
height=self.height) height=self.height)
@ -1206,9 +1048,9 @@ class bank(design.design):
def analytical_delay(self, slew, load): def analytical_delay(self, slew, load):
""" return analytical delay of the bank""" """ return analytical delay of the bank"""
msf_addr_delay = self.msf_address.analytical_delay(slew, self.decoder.input_load()) msf_addr_delay = self.msf_address.analytical_delay(slew, self.row_decoder.input_load())
decoder_delay = self.decoder.analytical_delay(msf_addr_delay.slew, self.wordline_driver.input_load()) decoder_delay = self.row_decoder.analytical_delay(msf_addr_delay.slew, self.wordline_driver.input_load())
word_driver_delay = self.wordline_driver.analytical_delay(decoder_delay.slew, self.bitcell_array.input_load()) word_driver_delay = self.wordline_driver.analytical_delay(decoder_delay.slew, self.bitcell_array.input_load())

View File

@ -33,6 +33,9 @@ class bitcell_array(design.design):
self.add_pins() self.add_pins()
self.create_layout() self.create_layout()
self.add_layout_pins() self.add_layout_pins()
self.offset_all_coordinates()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):

View File

@ -7,6 +7,7 @@ from pinv import pinv
from pnand2 import pnand2 from pnand2 import pnand2
from pnand3 import pnand3 from pnand3 import pnand3
from pnor2 import pnor2 from pnor2 import pnor2
from pinvbuf import pinvbuf
import math import math
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
@ -30,7 +31,7 @@ class control_logic(design.design):
self.create_modules() self.create_modules()
self.setup_layout_offsets() self.setup_layout_offsets()
self.add_modules() self.add_modules()
self.add_routing() #self.add_routing()
def create_modules(self): def create_modules(self):
""" add all the required modules """ """ add all the required modules """
@ -48,23 +49,18 @@ class control_logic(design.design):
self.add_mod(self.nor2) self.add_mod(self.nor2)
# Special gates: inverters for buffering # Special gates: inverters for buffering
self.clkbuf = pinvbuf(4,16)
self.add_mod(self.clkbuf)
self.inv = self.inv1 = pinv(1) self.inv = self.inv1 = pinv(1)
self.add_mod(self.inv1) self.add_mod(self.inv1)
self.inv2 = pinv(2) # self.inv2 = pinv(2)
self.add_mod(self.inv2) # self.add_mod(self.inv2)
self.inv4 = pinv(4) # self.inv4 = pinv(4)
self.add_mod(self.inv4) # self.add_mod(self.inv4)
self.inv8 = pinv(8) # self.inv8 = pinv(8)
self.add_mod(self.inv8) # self.add_mod(self.inv8)
self.inv16 = pinv(16) # self.inv16 = pinv(16)
self.add_mod(self.inv16) # self.add_mod(self.inv16)
c = reload(__import__(OPTS.ms_flop_array))
ms_flop_array = getattr(c, OPTS.ms_flop_array)
self.msf_control = ms_flop_array(name="msf_control",
columns=3,
word_size=3)
self.add_mod(self.msf_control)
c = reload(__import__(OPTS.replica_bitline)) c = reload(__import__(OPTS.replica_bitline))
replica_bitline = getattr(c, OPTS.replica_bitline) replica_bitline = getattr(c, OPTS.replica_bitline)
@ -91,8 +87,8 @@ class control_logic(design.design):
# First RAIL Parameters: gnd, oe, oebar, cs, we, clk_buf, clk_bar # First RAIL Parameters: gnd, oe, oebar, cs, we, clk_buf, clk_bar
self.rail_1_start_x = 0 self.rail_1_start_x = 0
self.num_rails_1 = 8 self.num_rails_1 = 7
self.rail_1_names = ["clk_buf", "gnd", "oe_bar", "cs", "we", "vdd", "oe", "clk_bar"] self.rail_1_names = ["clk_buf", "gnd", "cs", "we", "vdd", "oe", "clk_bar"]
self.overall_rail_1_gap = (self.num_rails_1 + 2) * self.m2_pitch self.overall_rail_1_gap = (self.num_rails_1 + 2) * self.m2_pitch
self.rail_1_x_offsets = {} self.rail_1_x_offsets = {}
@ -103,18 +99,21 @@ class control_logic(design.design):
def add_modules(self): def add_modules(self):
""" Place all the modules """ """ Place all the modules """
self.add_control_flops()
self.add_clk_buffer(0) self.add_clk_buffer(row=0) # 0 and 1st row
self.add_1st_row(0) self.add_oe_row(row=2)
self.add_2nd_row(self.inv1.height) self.add_sen_row(row=3)
self.add_3rd_row(2*self.inv1.height) self.add_we_row(row=4)
self.add_control_routing()
self.add_rbl(0) #self.add_control_routing()
self.add_rbl(row=5)
self.add_layout_pins() self.add_layout_pins()
self.add_lvs_correspondence_points() self.add_lvs_correspondence_points()
self.height = max(self.replica_bitline.width, 3 * self.inv1.height, self.msf_offset.y) self.height = max(self.replica_bitline.width, 4 * self.inv1.height)
self.width = self.replica_bitline_offset.x + self.replica_bitline.height self.width = self.replica_bitline_offset.x + self.replica_bitline.height
@ -122,7 +121,7 @@ class control_logic(design.design):
def add_routing(self): def add_routing(self):
""" Routing between modules """ """ Routing between modules """
self.add_clk_routing() #self.add_clk_routing()
self.add_trien_routing() self.add_trien_routing()
self.add_rblk_routing() self.add_rblk_routing()
self.add_wen_routing() self.add_wen_routing()
@ -130,116 +129,117 @@ class control_logic(design.design):
self.add_output_routing() self.add_output_routing()
self.add_supply_routing() self.add_supply_routing()
def add_control_flops(self):
""" Add the control signal flops for OEb, WEb, CSb. """
self.msf_offset = vector(0, self.inv.height+self.msf_control.width+2*self.m2_pitch)
self.msf_inst=self.add_inst(name="msf_control",
mod=self.msf_control,
offset=self.msf_offset,
rotate=270)
# don't change this order. This pins are meant for internal connection of msf array inside the control logic.
# These pins are connecting the msf_array inside of control_logic.
temp = ["oeb", "csb", "web",
"oe_bar", "oe",
"cs_bar", "cs",
"we_bar", "we",
"clk_buf", "vdd", "gnd"]
self.connect_inst(temp)
def add_rbl(self,y_off): def add_rbl(self,row):
""" Add the replica bitline """ """ Add the replica bitline """
y_off = row * self.inv1.height
# Add the RBL above the rows
# Add to the right of the control rows and routing channel # Add to the right of the control rows and routing channel
rows_end_x = max (self.row_1_end_x, self.row_2_end_x, self.row_3_end_x) self.replica_bitline_offset = vector(0, y_off)
self.replica_bitline_offset = vector(rows_end_x , y_off)
self.rbl=self.add_inst(name="replica_bitline", self.rbl=self.add_inst(name="replica_bitline",
mod=self.replica_bitline, mod=self.replica_bitline,
offset=self.replica_bitline_offset, offset=self.replica_bitline_offset)
mirror="MX",
rotate=90)
self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"]) self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"])
def add_layout_pins(self): def add_layout_pins(self):
""" Add the input/output layout pins. """ """ Add the input/output layout pins. """
# Top to bottom: CS WE OE signal groups # # Top to bottom: CS WE OE signal groups
pin_set = ["oeb","csb","web"] # pin_set = ["oeb","csb","web"]
for (i,pin_name) in zip(range(3),pin_set): # for (i,pin_name) in zip(range(3),pin_set):
subpin_name="din[{}]".format(i) # subpin_name="din[{}]".format(i)
pins=self.msf_inst.get_pins(subpin_name) # pins=self.msf_inst.get_pins(subpin_name)
for pin in pins: # for pin in pins:
if pin.layer=="metal3": # if pin.layer=="metal3":
self.add_layout_pin(text=pin_name, # self.add_layout_pin(text=pin_name,
layer="metal3", # layer="metal3",
offset=pin.ll(), # offset=pin.ll(),
width=pin.width(), # width=pin.width(),
height=pin.height()) # height=pin.height())
pin=self.clk_inv1.get_pin("A") pin=self.clkbuf.get_pin("A")
self.add_layout_pin(text="clk", self.add_layout_pin(text="clk",
layer="metal1", layer="metal1",
offset=pin.ll().scale(0,1), offset=pin.ll().scale(0,1),
width=pin.rx(), width=pin.rx(),
height=pin.height()) height=pin.height())
pin=self.clk_inv1.get_pin("gnd") # pin=self.clkbuf.get_pin("gnd")
self.add_layout_pin(text="gnd", # self.add_layout_pin(text="gnd",
layer="metal1", # layer="metal1",
offset=pin.ll(), # offset=pin.ll(),
width=self.width) # width=self.width)
pin=self.clk_inv1.get_pin("vdd") # pin=self.clkbuf.get_pin("vdd")
self.add_layout_pin(text="vdd", # self.add_layout_pin(text="vdd",
layer="metal1", # layer="metal1",
offset=pin.ll(), # offset=pin.ll(),
width=self.width) # width=self.width)
def add_clk_buffer(self,y_off): def add_clk_buffer(self,row):
""" Add the multistage clock buffer below the control flops """ """ Add the multistage clock buffer below the control flops """
# 4 stage clock buffer y_off = row*self.inv1.height
self.clk_inv1_offset = vector(0, y_off) if row % 2:
self.clk_inv1=self.add_inst(name="inv_clk1_bar", y_off += self.clkbuf.height
mod=self.inv2, mirror="MX"
offset=self.clk_inv1_offset) else:
self.connect_inst(["clk", "clk1_bar", "vdd", "gnd"]) mirror="R0"
self.clk_inv2_offset = self.clk_inv1_offset + vector(self.inv2.width,0)
self.clk_inv2=self.add_inst(name="inv_clk2",
mod=self.inv4,
offset=self.clk_inv2_offset)
self.connect_inst(["clk1_bar", "clk2", "vdd", "gnd"])
self.clk_bar_offset = self.clk_inv2_offset + vector(self.inv4.width,0)
self.clk_bar=self.add_inst(name="inv_clk_bar",
mod=self.inv8,
offset=self.clk_bar_offset)
self.connect_inst(["clk2", "clk_bar", "vdd", "gnd"])
self.clk_buf_offset = self.clk_bar_offset + vector(self.inv8.width,0)
self.clk_buf=self.add_inst(name="inv_clk_buf",
mod=self.inv16,
offset=self.clk_buf_offset)
self.connect_inst(["clk_bar", "clk_buf", "vdd", "gnd"])
# Connect between the inverters clkbuf_offset = vector(0,y_off)
self.add_path("metal1", [self.clk_inv1.get_pin("Z").center(), self.clkbuf_inst = self.add_inst(name="clkbuf",
self.clk_inv2.get_pin("A").center()]) mod=self.clkbuf,
self.add_path("metal1", [self.clk_inv2.get_pin("Z").center(), offset=clkbuf_offset)
self.clk_bar.get_pin("A").center()])
self.add_path("metal1", [self.clk_bar.get_pin("Z").center(),
self.clk_buf.get_pin("A").center()])
# This is the first rail offset self.connect_inst(["clk","clk_bar","clk","vdd","gnd"])
self.rail_1_start_x = max(self.msf_offset.x + self.msf_control.height,self.clk_buf_offset.x+self.inv16.width) + self.m2_pitch
# # 4 stage clock buffer
# self.clk_inv1_offset = vector(self.rail_1_start_x, y_off)
# self.clk_inv1=self.add_inst(name="inv_clk1_bar",
# mod=self.inv2,
# offset=self.clk_inv1_offset)
# self.connect_inst(["clk", "clk1_bar", "vdd", "gnd"])
# self.clk_inv2_offset = self.clk_inv1_offset + vector(self.inv2.width,0)
# self.clk_inv2=self.add_inst(name="inv_clk2",
# mod=self.inv4,
# offset=self.clk_inv2_offset)
# self.connect_inst(["clk1_bar", "clk2", "vdd", "gnd"])
# self.clk_bar_offset = self.clk_inv2_offset + vector(self.inv4.width,0)
# self.clk_bar=self.add_inst(name="inv_clk_bar",
# mod=self.inv8,
# offset=self.clk_bar_offset)
# self.connect_inst(["clk2", "clk_bar", "vdd", "gnd"])
# self.clk_buf_offset = self.clk_bar_offset + vector(self.inv8.width,0)
# self.clk_buf=self.add_inst(name="inv_clk_buf",
# mod=self.inv16,
# offset=self.clk_buf_offset)
# self.connect_inst(["clk_bar", "clk_buf", "vdd", "gnd"])
# # Connect between the inverters
# self.add_path("metal1", [self.clk_inv1.get_pin("Z").center(),
# self.clk_inv2.get_pin("A").center()])
# self.add_path("metal1", [self.clk_inv2.get_pin("Z").center(),
# self.clk_bar.get_pin("A").center()])
# self.add_path("metal1", [self.clk_bar.get_pin("Z").center(),
# self.clk_buf.get_pin("A").center()])
def add_1st_row(self,y_off):
x_off = self.rail_1_start_x + self.overall_rail_1_gap def add_rblk_row(self,row):
x_off = 0
y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height
mirror="MX"
else:
mirror="R0"
# input: OE, clk_bar,CS output: rblk_bar # input: OE, clk_bar,CS output: rblk_bar
self.rblk_bar_offset = vector(x_off, y_off) self.rblk_bar_offset = vector(x_off, y_off)
self.rblk_bar=self.add_inst(name="nand3_rblk_bar", self.rblk_bar=self.add_inst(name="nand3_rblk_bar",
mod=self.nand3, mod=self.nand3,
offset=self.rblk_bar_offset) offset=self.rblk_bar_offset,
mirror=mirror)
self.connect_inst(["clk_bar", "oe", "cs", "rblk_bar", "vdd", "gnd"]) self.connect_inst(["clk_bar", "oe", "cs", "rblk_bar", "vdd", "gnd"])
x_off += self.nand3.width x_off += self.nand3.width
@ -247,36 +247,21 @@ class control_logic(design.design):
self.rblk_offset = vector(x_off, y_off) self.rblk_offset = vector(x_off, y_off)
self.rblk=self.add_inst(name="inv_rblk", self.rblk=self.add_inst(name="inv_rblk",
mod=self.inv1, mod=self.inv1,
offset=self.rblk_offset) offset=self.rblk_offset,
mirror=mirror)
self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"]) self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"])
#x_off += self.inv1.width #x_off += self.inv1.width
self.row_1_end_x = x_off self.row_rblk_end_x = x_off
def add_2nd_row(self, y_off): def add_sen_row(self,row):
# start after first rails x_off = 0
x_off = self.rail_1_start_x + self.overall_rail_1_gap y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height y_off += self.inv1.height
mirror="MX"
# input: clk_buf, OE_bar output: tri_en else:
self.tri_en_offset = vector(x_off, y_off) mirror="R0"
self.tri_en=self.add_inst(name="nor2_tri_en",
mod=self.nor2,
offset=self.tri_en_offset,
mirror="MX")
self.connect_inst(["clk_buf", "oe_bar", "tri_en", "vdd", "gnd"])
x_off += self.nor2.width + self.cell_gap
# input: OE, clk_bar output: tri_en_bar
self.tri_en_bar_offset = vector(x_off,y_off)
self.tri_en_bar=self.add_inst(name="nand2_tri_en",
mod=self.nand2,
offset=self.tri_en_bar_offset,
mirror="MX")
self.connect_inst(["clk_bar", "oe", "tri_en_bar", "vdd", "gnd"])
x_off += self.nand2.width
x_off += self.inv1.width + self.cell_gap
# BUFFER INVERTERS FOR S_EN # BUFFER INVERTERS FOR S_EN
# input: input: pre_s_en_bar, output: s_en # input: input: pre_s_en_bar, output: s_en
@ -284,7 +269,7 @@ class control_logic(design.design):
self.s_en=self.add_inst(name="inv_s_en", self.s_en=self.add_inst(name="inv_s_en",
mod=self.inv1, mod=self.inv1,
offset=self.s_en_offset, offset=self.s_en_offset,
mirror="XY") mirror=mirror)
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"]) self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
x_off += self.inv1.width x_off += self.inv1.width
@ -293,25 +278,70 @@ class control_logic(design.design):
self.pre_s_en_bar=self.add_inst(name="inv_pre_s_en_bar", self.pre_s_en_bar=self.add_inst(name="inv_pre_s_en_bar",
mod=self.inv1, mod=self.inv1,
offset=self.pre_s_en_bar_offset, offset=self.pre_s_en_bar_offset,
mirror="XY") mirror=mirror)
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"]) self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
#x_off += self.inv1.width #x_off += self.inv1.width
self.row_sen_end_x = x_off
self.row_2_end_x = x_off def add_oe_row(self, row):
x_off = 0
y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height
mirror="MX"
else:
mirror="R0"
def add_3rd_row(self, y_off): # input: oe output: oe_bar
# start after first rails self.oe_inv_offset = vector(x_off, y_off)
x_off = self.rail_1_start_x + self.overall_rail_1_gap self.oe_inv=self.add_inst(name="oe_inv",
mod=self.inv1,
offset=self.oe_inv_offset,
mirror=mirror)
self.connect_inst(["oe", "oe_bar", "vdd", "gnd"])
x_off += self.inv1.width + self.cell_gap
# This prevents some M2 outputs from overlapping (hack)
x_off += self.inv1.width # input: clk_buf, OE_bar output: tri_en
self.tri_en_offset = vector(x_off, y_off)
self.tri_en=self.add_inst(name="nor2_tri_en",
mod=self.nor2,
offset=self.tri_en_offset,
mirror=mirror)
self.connect_inst(["clk_buf", "oe_bar", "tri_en", "vdd", "gnd"])
x_off += self.nor2.width + self.cell_gap
# input: OE, clk_bar output: tri_en_bar
self.tri_en_bar_offset = vector(x_off,y_off)
self.tri_en_bar=self.add_inst(name="nand2_tri_en",
mod=self.nand2,
offset=self.tri_en_bar_offset,
mirror=mirror)
self.connect_inst(["clk_bar", "oe", "tri_en_bar", "vdd", "gnd"])
#x_off += self.nand2.width
#x_off += self.inv1.width + self.cell_gap
self.row_oe_end_x = x_off
def add_we_row(self,row):
x_off = 0
y_off = row*self.inv1.height
if row % 2:
y_off += self.inv1.height
mirror="MX"
else:
mirror="R0"
# input: WE, clk_bar, CS output: w_en_bar # input: WE, clk_bar, CS output: w_en_bar
self.w_en_bar_offset = vector(x_off, y_off) self.w_en_bar_offset = vector(x_off, y_off)
self.w_en_bar=self.add_inst(name="nand3_w_en_bar", self.w_en_bar=self.add_inst(name="nand3_w_en_bar",
mod=self.nand3, mod=self.nand3,
offset=self.w_en_bar_offset) offset=self.w_en_bar_offset,
mirror=mirror)
self.connect_inst(["clk_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]) self.connect_inst(["clk_bar", "cs", "we", "w_en_bar", "vdd", "gnd"])
x_off += self.nand3.width x_off += self.nand3.width
@ -319,7 +349,9 @@ class control_logic(design.design):
self.pre_w_en_offset = vector(x_off, y_off) self.pre_w_en_offset = vector(x_off, y_off)
self.pre_w_en=self.add_inst(name="inv_pre_w_en", self.pre_w_en=self.add_inst(name="inv_pre_w_en",
mod=self.inv1, mod=self.inv1,
offset=self.pre_w_en_offset) offset=self.pre_w_en_offset,
mirror=mirror)
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
x_off += self.inv1.width x_off += self.inv1.width
@ -328,70 +360,64 @@ class control_logic(design.design):
self.pre_w_en_bar_offset = vector(x_off, y_off) self.pre_w_en_bar_offset = vector(x_off, y_off)
self.pre_w_en_bar=self.add_inst(name="inv_pre_w_en_bar", self.pre_w_en_bar=self.add_inst(name="inv_pre_w_en_bar",
mod=self.inv1, mod=self.inv1,
offset=self.pre_w_en_bar_offset) offset=self.pre_w_en_bar_offset,
mirror=mirror)
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"]) self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
x_off += self.inv1.width x_off += self.inv1.width
self.w_en_offset = vector(x_off, y_off) self.w_en_offset = vector(x_off, y_off)
self.w_en=self.add_inst(name="inv_w_en2", self.w_en=self.add_inst(name="inv_w_en2",
mod=self.inv1, mod=self.inv1,
offset=self.w_en_offset) offset=self.w_en_offset,
mirror=mirror)
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"]) self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
#x_off += self.inv1.width #x_off += self.inv1.width
self.row_3_end_x = x_off self.row_we_end_x = x_off
def add_control_routing(self): # def add_control_routing(self):
""" Route the vertical rails for internal control signals """ # """ Route the vertical rails for internal control signals """
control_rail_height = max(3 * self.inv1.height, self.msf_offset.y) # control_rail_height = 3*self.inv1.height
for i in range(self.num_rails_1): # for i in range(self.num_rails_1):
offset = vector(self.rail_1_start_x + (i+1) * self.m2_pitch,0) # offset = vector(self.rail_1_start_x + (i+1) * self.m2_pitch,0)
if self.rail_1_names[i] in ["clk_buf", "clk_bar", "vdd", "gnd"]: # if self.rail_1_names[i] in ["clk_buf", "clk_bar", "vdd", "gnd"]:
self.add_layout_pin(text=self.rail_1_names[i], # self.add_layout_pin(text=self.rail_1_names[i],
layer="metal2", # layer="metal2",
offset=offset, # offset=offset,
width=drc["minwidth_metal2"], # width=drc["minwidth_metal2"],
height=control_rail_height) # height=control_rail_height)
else: # else:
# just for LVS correspondence... # # just for LVS correspondence...
self.add_label_pin(text=self.rail_1_names[i], # self.add_label_pin(text=self.rail_1_names[i],
layer="metal2", # layer="metal2",
offset=offset, # offset=offset,
width=drc["minwidth_metal2"], # width=drc["minwidth_metal2"],
height=control_rail_height) # height=control_rail_height)
self.rail_1_x_offsets[self.rail_1_names[i]]=offset.x + 0.5*drc["minwidth_metal2"] # center offset # self.rail_1_x_offsets[self.rail_1_names[i]]=offset.x + 0.5*drc["minwidth_metal2"] # center offset
# pins are in order ["oeb","csb","web"] # 0 1 2
self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[0]","oe")
self.connect_rail_from_left_m2m3(self.msf_inst,"dout[0]","oe_bar")
self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[1]","cs")
self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[2]","we")
# Connect the gnd and vdd of the control
gnd_pins = self.msf_inst.get_pins("gnd")
for p in gnd_pins:
if p.layer != "metal2":
continue
gnd_pin = p.rc()
gnd_rail_position = vector(self.rail_1_x_offsets["gnd"], gnd_pin.y)
self.add_wire(("metal3","via2","metal2"),[gnd_pin, gnd_rail_position])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=gnd_pin,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=gnd_rail_position,
rotate=90)
vdd_pins = self.msf_inst.get_pins("vdd")
for p in vdd_pins:
if p.layer != "metal1":
continue
clk_vdd_position = vector(p.bc().x,self.clk_buf.get_pin("vdd").uy())
self.add_path("metal1",[p.bc(),clk_vdd_position])
# # # Connect the gnd and vdd of the control
# # gnd_pins = self.msf_inst.get_pins("gnd")
# # for p in gnd_pins:
# # if p.layer != "metal2":
# # continue
# # gnd_pin = p.rc()
# # gnd_rail_position = vector(self.rail_1_x_offsets["gnd"], gnd_pin.y)
# # self.add_wire(("metal3","via2","metal2"),[gnd_pin, gnd_rail_position])
# # self.add_via_center(layers=("metal2","via2","metal3"),
# # offset=gnd_pin,
# # rotate=90)
# # self.add_via_center(layers=("metal2","via2","metal3"),
# # offset=gnd_rail_position,
# # rotate=90)
# # vdd_pins = self.msf_inst.get_pins("vdd")
# # for p in vdd_pins:
# # if p.layer != "metal1":
# # continue
# # clk_vdd_position = vector(p.bc().x,self.clk_buf.get_pin("vdd").uy())
# # self.add_path("metal1",[p.bc(),clk_vdd_position])
@ -482,8 +508,13 @@ class control_logic(design.design):
def add_trien_routing(self): def add_trien_routing(self):
self.connect_rail_from_right(self.oe_inv,"A","oe")
self.connect_rail_from_right(self.tri_en,"A","clk_buf") self.connect_rail_from_right(self.tri_en,"A","clk_buf")
self.connect_rail_from_right(self.tri_en,"B","oe_bar") oe_inv_out_pos = self.oe_inv.get_pin("Z").ul()
in_pos = self.tri_en.get_pin("B").rc()
mid1 = vector(oe_inv_out_pos.x,in_pos.y)
self.add_path("metal1",[oe_inv_out_pos,mid1,in_pos])
self.connect_rail_from_right_m2m3(self.tri_en_bar,"A","clk_bar") self.connect_rail_from_right_m2m3(self.tri_en_bar,"A","clk_bar")
self.connect_rail_from_right_m2m3(self.tri_en_bar,"B","oe") self.connect_rail_from_right_m2m3(self.tri_en_bar,"B","oe")
@ -500,43 +531,43 @@ class control_logic(design.design):
self.add_path("metal1",[self.pre_s_en_bar.get_pin("Z").center(), self.s_en.get_pin("A").center()]) self.add_path("metal1",[self.pre_s_en_bar.get_pin("Z").center(), self.s_en.get_pin("A").center()])
def add_clk_routing(self): # def add_clk_routing(self):
""" Route the clk and clk_bar signal internally """ # """ Route the clk and clk_bar signal internally """
# clk_buf # # clk_buf
clk_buf_pos = self.clk_buf.get_pin("Z").rc() # clk_buf_pos = self.clk_buf.get_pin("Z").rc()
clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], clk_buf_pos.y) # clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], clk_buf_pos.y)
self.add_wire(("metal1","via1","metal2"),[clk_buf_pos, clk_buf_rail_position]) # self.add_wire(("metal1","via1","metal2"),[clk_buf_pos, clk_buf_rail_position])
self.add_via_center(layers=("metal1","via1","metal2"), # self.add_via_center(layers=("metal1","via1","metal2"),
offset=clk_buf_rail_position, # offset=clk_buf_rail_position,
rotate=90) # rotate=90)
# clk_bar, routes over the clock buffer vdd rail # # clk_bar, routes over the clock buffer vdd rail
clk_pin = self.clk_bar.get_pin("Z") # clk_pin = self.clk_bar.get_pin("Z")
vdd_pin = self.clk_bar.get_pin("vdd") # vdd_pin = self.clk_bar.get_pin("vdd")
# move the output pin up to metal2 # # move the output pin up to metal2
self.add_via_center(layers=("metal1","via1","metal2"), # self.add_via_center(layers=("metal1","via1","metal2"),
offset=clk_pin.rc(), # offset=clk_pin.rc(),
rotate=90) # rotate=90)
# route to a position over the supply rail # # route to a position over the supply rail
in_pos = vector(clk_pin.rx(), vdd_pin.cy()) # in_pos = vector(clk_pin.rx(), vdd_pin.cy())
self.add_path("metal2",[clk_pin.rc(), in_pos]) # self.add_path("metal2",[clk_pin.rc(), in_pos])
# connect that position to the control bus # # connect that position to the control bus
rail_pos = vector(self.rail_1_x_offsets["clk_bar"], in_pos.y) # rail_pos = vector(self.rail_1_x_offsets["clk_bar"], in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos]) # self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal2","via2","metal3"), # self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos, # offset=in_pos,
rotate=90) # rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"), # self.add_via_center(layers=("metal2","via2","metal3"),
offset=rail_pos, # offset=rail_pos,
rotate=90) # rotate=90)
# clk_buf to msf control flops # # clk_buf to msf control flops
msf_clk_pos = self.msf_inst.get_pin("clk").bc() # msf_clk_pos = self.msf_inst.get_pin("clk").bc()
mid1 = msf_clk_pos - vector(0,self.m2_pitch) # mid1 = msf_clk_pos - vector(0,self.m2_pitch)
clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], mid1.y) # clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], mid1.y)
# route on M2 to allow vdd connection # # route on M2 to allow vdd connection
self.add_wire(("metal2","via1","metal1"),[msf_clk_pos, mid1, clk_buf_rail_position]) # self.add_wire(("metal2","via1","metal1"),[msf_clk_pos, mid1, clk_buf_rail_position])
def connect_right_pin_to_output_pin(self, inst, pin_name, out_name): def connect_right_pin_to_output_pin(self, inst, pin_name, out_name):
""" Create an output pin on the bottom side from the pin of a given instance. """ """ Create an output pin on the bottom side from the pin of a given instance. """
@ -580,7 +611,7 @@ class control_logic(design.design):
well_width = drc["minwidth_well"] well_width = drc["minwidth_well"]
# M1 gnd rail from inv1 to max # M1 gnd rail from inv1 to max
start_offset = self.clk_inv1.get_pin("gnd").lc() start_offset = self.clkbuf.get_pin("gnd").lc()
row1_gnd_end_offset = vector(rows_end,start_offset.y) row1_gnd_end_offset = vector(rows_end,start_offset.y)
self.add_path("metal1",[start_offset,row1_gnd_end_offset]) self.add_path("metal1",[start_offset,row1_gnd_end_offset])
rail_position = vector(self.rail_1_x_offsets["gnd"], start_offset.y) rail_position = vector(self.rail_1_x_offsets["gnd"], start_offset.y)
@ -597,7 +628,7 @@ class control_logic(design.design):
height=well_width) height=well_width)
# M1 vdd rail from inv1 to max # M1 vdd rail from inv1 to max
start_offset = self.clk_inv1.get_pin("vdd").lc() start_offset = self.clkbuf.get_pin("vdd").lc()
row1_vdd_end_offset = vector(rows_end,start_offset.y) row1_vdd_end_offset = vector(rows_end,start_offset.y)
self.add_path("metal1",[start_offset,row1_vdd_end_offset]) self.add_path("metal1",[start_offset,row1_vdd_end_offset])
rail_position = vector(self.rail_1_x_offsets["vdd"], start_offset.y) rail_position = vector(self.rail_1_x_offsets["vdd"], start_offset.y)
@ -667,19 +698,19 @@ class control_logic(design.design):
These should probably be turned off by default though, since extraction These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist. will show these as ports in the extracted netlist.
""" """
pin=self.clk_inv1.get_pin("Z") # pin=self.clk_inv1.get_pin("Z")
self.add_label_pin(text="clk1_bar", # self.add_label_pin(text="clk1_bar",
layer="metal1", # layer="metal1",
offset=pin.ll(), # offset=pin.ll(),
height=pin.height(), # height=pin.height(),
width=pin.width()) # width=pin.width())
pin=self.clk_inv2.get_pin("Z") # pin=self.clk_inv2.get_pin("Z")
self.add_label_pin(text="clk2", # self.add_label_pin(text="clk2",
layer="metal1", # layer="metal1",
offset=pin.ll(), # offset=pin.ll(),
height=pin.height(), # height=pin.height(),
width=pin.width()) # width=pin.width())
pin=self.rbl.get_pin("out") pin=self.rbl.get_pin("out")
self.add_label_pin(text="out", self.add_label_pin(text="out",

View File

@ -34,7 +34,7 @@ class delay_chain(design.design):
self.add_pins() self.add_pins()
self.create_module() self.create_module()
self.route_inv() self.route_inverters()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
@ -48,82 +48,69 @@ class delay_chain(design.design):
def create_module(self): def create_module(self):
""" Add the inverter logical module """ """ Add the inverter logical module """
self.create_inv_list()
self.inv = pinv(route_output=False) self.inv = pinv(route_output=False)
self.add_mod(self.inv) self.add_mod(self.inv)
# half chain length is the width of the layout # half chain length is the width of the layout
# invs are stacked into 2 levels so input/output are close # invs are stacked into 2 levels so input/output are close
# extra metal is for the gnd connection U # 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 = len(self.fanout_list)*self.inv.height
self.height = 2 * 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 def add_inverters(self):
self.inv_list.append([stage_num+1, False])
def add_inv_list(self):
""" Add the inverters and connect them based on the stage list """ """ Add the inverters and connect them based on the stage list """
dummy_load_counter = 1 self.driver_inst_list = []
self.inv_inst_list = [] self.rightest_load_inst = {}
for i in range(self.num_inverters): self.load_inst_map = {}
# First place the gates for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
if i < self.num_top_half: if stage_num % 2:
# add top level that is upside down inv_mirror = "MX"
inv_offset = vector(i * self.inv.width, 2 * self.inv.height) inv_offset = vector(0, (stage_num+1)* self.inv.height)
inv_mirror="MX"
else: else:
# add bottom level from right to left inv_mirror = "R0"
inv_offset = vector((self.num_inverters - i) * self.inv.width, 0) inv_offset = vector(0, stage_num * self.inv.height)
inv_mirror="MY"
cur_inv=self.add_inst(name="dinv{}".format(i), # Add the inverter
cur_driver=self.add_inst(name="dinv{}".format(stage_num),
mod=self.inv, mod=self.inv,
offset=inv_offset, offset=inv_offset,
mirror=inv_mirror) mirror=inv_mirror)
# keep track of the inverter instances so we can use them to get the pins # 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] # Hook up the driver
next_stage = self.inv_list[i][0]+1 if stage_num+1==len(self.fanout_list):
if i == 0: stageout_name = "out"
input = "in"
else: else:
input = "s{}".format(cur_stage) stageout_name = "dout_{}".format(stage_num+1)
if i == self.num_inverters-1: if stage_num == 0:
output = "out" stagein_name = "in"
else: else:
output = "s{}".format(next_stage) stagein_name = "dout_{}".format(stage_num)
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
# if the gate is a dummy load don't connect the output # Now add the dummy loads to the right
# else reset the counter self.load_inst_map[cur_driver]=[]
if self.inv_list[i][1]: for i in range(fanout_size):
output = output+"n{0}".format(dummy_load_counter) inv_offset += vector(self.inv.width,0)
dummy_load_counter += 1 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: else:
dummy_load_counter = 1 # Keep track of the last one so we can add the the wire later
self.rightest_load_inst[cur_driver]=cur_load
self.connect_inst(args=[input, output, "vdd", "gnd"])
if i != 0:
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=cur_inv.get_pin("A").center())
def add_route(self, pin1, pin2): def add_route(self, pin1, pin2):
""" This guarantees that we route from the top to bottom row correctly. """ """ This guarantees that we route from the top to bottom row correctly. """
pin1_pos = pin1.center() pin1_pos = pin1.center()
@ -135,79 +122,88 @@ class delay_chain(design.design):
# Written this way to guarantee it goes right first if we are switching rows # 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]) 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 """ """ 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"), for i in range(len(self.driver_inst_list)):
offset=start_inv_inst.get_pin("Z").center()), 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(),
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=a_pin.center(),
rotate=90)
# route from output to first load # Route an M3 horizontal wire to the furthest
start_inv_pin = start_inv_inst.get_pin("Z") z_pin = inv.get_pin("Z")
load_inst = self.inv_inst_list[start_inv+1] a_pin = inv.get_pin("A")
load_pin = load_inst.get_pin("A") a_max = self.rightest_load_inst[inv].get_pin("A")
self.add_route(start_inv_pin, load_pin) self.add_via_center(layers=("metal1","via1","metal2"),
offset=a_pin.center(),
rotate=90)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=z_pin.center(),
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=z_pin.center(),
rotate=90)
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()])
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
def add_layout_pins(self): def add_layout_pins(self):
""" Add vdd and gnd rails and the input/output. Connect the gnd rails internally on """ Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
the top end with no input/output to obstruct. """ the top end with no input/output to obstruct. """
vdd_pin = self.inv.get_pin("vdd")
gnd_pin = self.inv.get_pin("gnd") for driver in self.driver_inst_list:
for i in range(3): vdd_pin = driver.get_pin("vdd")
(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", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=offset + vdd_pin.ll().scale(1,y_dir), offset=vdd_pin.ll(),
width=rail_width, width=self.width,
height=drc["minwidth_metal1"]) height=vdd_pin.height())
else: gnd_pin = driver.get_pin("gnd")
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=offset + gnd_pin.ll().scale(1,y_dir), offset=gnd_pin.ll(),
width=rail_width, width=self.width,
height=drc["minwidth_metal1"]) height=gnd_pin.height())
# 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])
# input is A pin of first inverter # 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(),
rotate=90)
self.add_layout_pin(text="in", self.add_layout_pin(text="in",
layer="metal1", layer="metal2",
offset=a_pin.ll(), offset=a_pin.ll().scale(1,0),
width=a_pin.width(), width=a_pin.width(),
height=a_pin.height()) height=a_pin.cy())
# output is Z pin of last inverter # output is A pin of last load inverter
z_pin = self.inv_inst_list[-1].get_pin("Z") 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(),
rotate=90)
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(text="out", self.add_layout_pin(text="out",
layer="metal1", layer="metal2",
offset=z_pin.ll().scale(0,1), offset=mid_point.scale(1,0))
width=z_pin.lx())

View File

@ -4,7 +4,6 @@ from tech import drc
from math import log from math import log
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
import dff_buf
class dff_array(design.design): class dff_array(design.design):
""" """
@ -21,7 +20,9 @@ class dff_array(design.design):
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.dff = dff_buf.dff_buf(inv1_size, inv2_size) c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, OPTS.dff)
self.dff = self.mod_dff("dff")
self.add_mod(self.dff) self.add_mod(self.dff)
self.width = self.columns * self.dff.width self.width = self.columns * self.dff.width
@ -42,7 +43,6 @@ class dff_array(design.design):
for y in range(self.rows): for y in range(self.rows):
for x in range(self.columns): for x in range(self.columns):
self.add_pin(self.get_dout_name(y,x)) 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("clk")
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
@ -64,7 +64,6 @@ class dff_array(design.design):
mirror=mirror) mirror=mirror)
self.connect_inst([self.get_din_name(y,x), self.connect_inst([self.get_din_name(y,x),
self.get_dout_name(y,x), self.get_dout_name(y,x),
self.get_dout_bar_name(y,x),
"clk", "clk",
"vdd", "vdd",
"gnd"]) "gnd"])
@ -89,15 +88,6 @@ class dff_array(design.design):
return dout_name 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): def add_layout_pins(self):
@ -138,14 +128,6 @@ class dff_array(design.design):
height=dout_pin.height()) 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 # Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk") clk_pin = self.dff_insts[0,0].get_pin("clk")

View File

@ -0,0 +1,180 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import dff_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):
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)

View File

@ -52,9 +52,14 @@ class hierarchical_decoder(design.design):
self.nand3 = pnand3() self.nand3 = pnand3()
self.add_mod(self.nand3) self.add_mod(self.nand3)
# CREATION OF PRE-DECODER self.add_decoders()
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.pre2_4 = pre2x4()
self.add_mod(self.pre2_4) self.add_mod(self.pre2_4)
self.pre3_8 = pre3x8() self.pre3_8 = pre3x8()
self.add_mod(self.pre3_8) self.add_mod(self.pre3_8)

View File

@ -21,8 +21,6 @@ class hierarchical_predecode(design.design):
c = reload(__import__(OPTS.bitcell)) c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell) self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell_height = self.mod_bitcell.height
def add_pins(self): def add_pins(self):
for k in range(self.number_of_inputs): for k in range(self.number_of_inputs):

View File

@ -13,8 +13,8 @@ class options(optparse.Values):
# This is the name of the technology. # This is the name of the technology.
tech_name = "" tech_name = ""
# This is the temp directory where all intermediate results are stored. # This is the temp directory where all intermediate results are stored.
openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid()) #openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid())
#openram_temp = "/Users/{}/openram_temp/".format(getpass.getuser()) openram_temp = "/Users/{}/openram_temp/".format(getpass.getuser())
# This is the verbosity level to control debug information. 0 is none, 1 # This is the verbosity level to control debug information. 0 is none, 1
# is minimal, etc. # is minimal, etc.
debug_level = 0 debug_level = 0
@ -59,7 +59,7 @@ class options(optparse.Values):
ms_flop = "ms_flop" ms_flop = "ms_flop"
ms_flop_array = "ms_flop_array" ms_flop_array = "ms_flop_array"
dff = "dff" dff = "dff"
dff_array = "dff_array" dff_array = "dff_buf_array"
control_logic = "control_logic" control_logic = "control_logic"
bitcell_array = "bitcell_array" bitcell_array = "bitcell_array"
sense_amp = "sense_amp" sense_amp = "sense_amp"

159
compiler/pgates/pinvbuf.py Normal file
View File

@ -0,0 +1,159 @@
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 pinvbuf(design.design):
"""
This is a simple inverter/buffer used for driving loads. It is
used in the column decoder for 1:2 decoding and as the clock buffer.
"""
def __init__(self, inv1_size=2, inv2_size=4, name=""):
if name=="":
name = "pinvbuf_{0}_{1}".format(inv1_size, inv2_size)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
self.inv = pinv(size=1)
self.add_mod(self.inv)
self.inv1 = pinv(size=inv1_size)
self.add_mod(self.inv1)
self.inv2 = pinv(size=inv2_size)
self.add_mod(self.inv2)
self.width = self.inv1.width + self.inv2.width
self.height = 2*self.inv1.height
self.create_layout()
self.offset_all_coordinates()
self.DRC_LVS()
def create_layout(self):
self.add_pins()
self.add_insts()
self.add_wires()
self.add_layout_pins()
def add_pins(self):
self.add_pin("A")
self.add_pin("Zb")
self.add_pin("Z")
self.add_pin("vdd")
self.add_pin("gnd")
def add_insts(self):
# Add INV1 to the right (capacitance shield)
self.inv1_inst=self.add_inst(name="buf_inv1",
mod=self.inv,
offset=vector(0,0))
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
# Add INV2 to the right
self.inv2_inst=self.add_inst(name="buf_inv2",
mod=self.inv1,
offset=vector(self.inv1_inst.rx(),0))
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
# Add INV3 to the right
self.inv3_inst=self.add_inst(name="buf_inv3",
mod=self.inv2,
offset=vector(self.inv2_inst.rx(),0))
self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
# Add INV4 to the bottom
self.inv4_inst=self.add_inst(name="buf_inv4",
mod=self.inv2,
offset=vector(self.inv2_inst.rx(),2*self.inv2.height),
mirror = "MX")
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def add_wires(self):
# 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()])
# inv2 Z to inv3 A
z2_pin = self.inv2_inst.get_pin("Z")
a3_pin = self.inv3_inst.get_pin("A")
mid_point = vector(z2_pin.cx(), a3_pin.cy())
self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()])
# inv1 Z to inv4 A (up and over)
z1_pin = self.inv1_inst.get_pin("Z")
a4_pin = self.inv4_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a4_pin.cy())
self.add_wire(("metal1","via1","metal2"), [z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=z1_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
# Continous vdd rail along with label.
gnd_pin=self.inv4_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
width=self.width,
height=gnd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
z_pin = self.inv4_inst.get_pin("Z")
self.add_layout_pin_center_rect(text="Z",
layer="metal2",
offset=z_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=z_pin.center())
zb_pin = self.inv3_inst.get_pin("Z")
self.add_layout_pin_center_rect(text="Zb",
layer="metal2",
offset=zb_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=zb_pin.center())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_center_rect(text="A",
layer="metal2",
offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a_pin.center())
def analytical_delay(self, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
inv1_delay = self.inv1.analytical_delay(slew=slew, load=self.inv2.input_load())
inv2_delay = self.inv2.analytical_delay(slew=inv1_delay.slew, load=load)
return inv1_delay + inv2_delay

View File

@ -22,8 +22,8 @@ class sram(design.design):
c = reload(__import__(OPTS.control_logic)) c = reload(__import__(OPTS.control_logic))
self.mod_control_logic = getattr(c, OPTS.control_logic) self.mod_control_logic = getattr(c, OPTS.control_logic)
c = reload(__import__(OPTS.ms_flop_array)) c = reload(__import__(OPTS.dff_array))
self.mod_ms_flop_array = getattr(c, OPTS.ms_flop_array) self.mod_dff_array = getattr(c, OPTS.dff_array)
c = reload(__import__(OPTS.bitcell)) c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell) self.mod_bitcell = getattr(c, OPTS.bitcell)
@ -146,10 +146,10 @@ class sram(design.design):
self.add_pin("ADDR[{0}]".format(i),"INPUT") self.add_pin("ADDR[{0}]".format(i),"INPUT")
# These are used to create the physical pins too # These are used to create the physical pins too
self.control_logic_inputs=["CSb", "WEb", "OEb", "clk"] self.control_logic_inputs=["CSb", "WEb", "OEb"]
self.control_logic_outputs=["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar", "clk_buf"] self.control_logic_outputs=["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar", "clk_buf"]
self.add_pin_list(self.control_logic_inputs,"INPUT") self.add_pin_list(self.control_logic_inputs + ["clk"],"INPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd","GROUND")
@ -210,7 +210,7 @@ class sram(design.design):
""" Route the shared signals for two and four bank configurations. """ """ Route the shared signals for two and four bank configurations. """
# create the input control pins # create the input control pins
for n in self.control_logic_inputs: for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n.lower(), n) self.copy_layout_pin(self.control_logic_inst, n.lower(), n)
# connect the control logic to the control bus # connect the control logic to the control bus
@ -374,7 +374,7 @@ class sram(design.design):
length=self.vertical_bus_height, length=self.vertical_bus_height,
vertical=True) vertical=True)
self.addr_bus_names=["ADDR[{}]".format(i) for i in range(self.addr_size)] self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)]
self.vert_control_bus_positions.update(self.create_bus(layer="metal2", self.vert_control_bus_positions.update(self.create_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=self.addr_bus_offset, offset=self.addr_bus_offset,
@ -797,6 +797,11 @@ class sram(design.design):
self.control_logic = self.mod_control_logic(num_rows=self.num_rows) self.control_logic = self.mod_control_logic(num_rows=self.num_rows)
self.add_mod(self.control_logic) self.add_mod(self.control_logic)
# Create the address and control flops
dff_size = self.addr_size + len(self.control_logic_inputs)
self.addr_ctrl_dff = self.mod_dff_array(rows=dff_size, columns=1)
self.add_mod(self.addr_ctrl_dff)
# Create the bank module (up to four are instantiated) # Create the bank module (up to four are instantiated)
self.bank = bank(word_size=self.word_size, self.bank = bank(word_size=self.word_size,
num_words=self.num_words_per_bank, num_words=self.num_words_per_bank,
@ -805,14 +810,13 @@ class sram(design.design):
name="bank") name="bank")
self.add_mod(self.bank) self.add_mod(self.bank)
# Conditionally create the # Create bank decoder
if(self.num_banks > 1): if(self.num_banks > 1):
self.create_multi_bank_modules() self.create_multi_bank_modules()
self.bank_count = 0 self.bank_count = 0
self.supply_rail_width = self.bank.supply_rail_width self.supply_rail_width = self.bank.supply_rail_width
# Leave some extra space for the pitch
self.supply_rail_pitch = self.bank.supply_rail_pitch self.supply_rail_pitch = self.bank.supply_rail_pitch
@ -900,13 +904,32 @@ class sram(design.design):
return line_positions return line_positions
def add_control_addr_dff(self, position, rotate=0):
""" Add and place address and control flops """
self.addr_ctrl_dff_inst = self.add_inst(name="address",
mod=self.addr_ctrl_dff,
offset=position,
rotate=rotate)
# inputs, outputs/output/bar
inputs = []
outputs = []
for i in range(self.addr_size):
inputs.append("ADDR[{}]".format(i))
outputs.append("A[{}]".format(i))
for i in self.control_logic_inputs:
inputs.append(i)
outputs.append(i+"_s")
self.connect_inst(inputs + outputs + ["clk", "vdd", "gnd"])
def add_control_logic(self, position, rotate): def add_control_logic(self, position, rotate):
""" Add and place control logic """ """ Add and place control logic """
self.control_logic_inst=self.add_inst(name="control", self.control_logic_inst=self.add_inst(name="control",
mod=self.control_logic, mod=self.control_logic,
offset=position, offset=position,
rotate=rotate) rotate=rotate)
self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"]) self.connect_inst(self.control_logic_inputs + ["clk"] + self.control_logic_outputs + ["vdd", "gnd"])
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
@ -938,10 +961,13 @@ class sram(design.design):
# are not recomputed using instance placement. So, place the control logic such that it aligns # are not recomputed using instance placement. So, place the control logic such that it aligns
# with the top of the SRAM. # with the top of the SRAM.
control_gap = 2*self.m3_width control_gap = 2*self.m3_width
pos = vector(-control_gap, control_pos = vector(-control_gap,
self.bank.height-self.control_logic.width) self.bank.height-self.control_logic.width)
self.add_control_logic(position=pos, self.add_control_logic(position=control_pos, rotate=90)
rotate=90)
addr_pos = vector(self.control_logic_inst.lx(),
2*self.supply_rail_pitch)
self.add_control_addr_dff(addr_pos)
self.width = self.bank.width + self.control_logic.height + control_gap self.width = self.bank.width + self.control_logic.height + control_gap
self.height = self.bank.height self.height = self.bank.height
@ -955,7 +981,7 @@ class sram(design.design):
self.copy_layout_pin(self.bank_inst, "DATA[{}]".format(i)) self.copy_layout_pin(self.bank_inst, "DATA[{}]".format(i))
for i in range(self.addr_size): for i in range(self.addr_size):
self.copy_layout_pin(self.bank_inst, "ADDR[{}]".format(i)) self.copy_layout_pin(self.bank_inst, "A[{}]".format(i))
for (old,new) in zip(["csb","web","oeb","clk"],["CSb","WEb","OEb","clk"]): for (old,new) in zip(["csb","web","oeb","clk"],["CSb","WEb","OEb","clk"]):
self.copy_layout_pin(self.control_logic_inst, old, new) self.copy_layout_pin(self.control_logic_inst, old, new)

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python2.7
"""
Run a regresion test on a 2-row buffer cell
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class pinvbuf_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify
import verify
OPTS.check_lvsdrc = False
import pinvbuf
debug.info(2, "Testing inverter/buffer 4x 8x")
a = pinvbuf.pinvbuf(4,8)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python2.7
"""
Run a regresion test on a dff_array.
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class dff_buf_array_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify
import verify
OPTS.check_lvsdrc = False
import dff_buf_array
debug.info(2, "Testing dff_buf_array for 3x3")
a = dff_buf_array.dff_buf_array(rows=3, columns=3)
self.local_check(a)
debug.info(2, "Testing dff_buf_array for 1x3")
a = dff_buf_array.dff_buf_array(rows=1, columns=3)
self.local_check(a)
debug.info(2, "Testing dff_buf_array for 3x1")
a = dff_buf_array.dff_buf_array(rows=3, columns=1)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -34,9 +34,9 @@ class single_bank_test(openram_test):
self.local_check(a) self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail # Eight way has a short circuit of one column mux select to gnd rail
# debug.info(1, "Eight way column mux") debug.info(1, "Eight way column mux")
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4") a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4")
# self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()