mirror of https://github.com/VLSIDA/OpenRAM.git
Reworking control logic for veritcal poly. Rewrote delay line. Rewrote buffered-DFF array.
This commit is contained in:
parent
c020d74f26
commit
ed8eaed54f
|
|
@ -544,6 +544,109 @@ class layout(lef.lef):
|
|||
width=xmax-xmin,
|
||||
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):
|
||||
# NOTE: Currently does not work (Needs further research)
|
||||
#self.pdf_name = self.name + ".pdf"
|
||||
|
|
|
|||
|
|
@ -182,10 +182,12 @@ class pin_layout:
|
|||
width=self.width(),
|
||||
height=self.height(),
|
||||
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,
|
||||
layerNumber=layer[self.layer],
|
||||
purposeNumber=0,
|
||||
offsetInMicrons=self.ll(),
|
||||
offsetInMicrons=self.center(),
|
||||
magnification=GDS["zoom"],
|
||||
rotate=None)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,12 +9,15 @@ from pinv import pinv
|
|||
from pnand2 import pnand2
|
||||
from pnor2 import pnor2
|
||||
from vector import vector
|
||||
from pinvbuf import pinvbuf
|
||||
|
||||
from globals import OPTS
|
||||
|
||||
class bank(design.design):
|
||||
"""
|
||||
Dynamically generated a single Bank including bitcell array,
|
||||
hierarchical_decoder, precharge, column_mux, write driver and sense amplifiers.
|
||||
Dynamically generated a single bank including bitcell array,
|
||||
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=""):
|
||||
|
|
@ -40,7 +43,8 @@ class bank(design.design):
|
|||
self.num_banks = num_banks
|
||||
|
||||
# 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:
|
||||
self.prefix="gated_"
|
||||
else:
|
||||
|
|
@ -51,13 +55,14 @@ class bank(design.design):
|
|||
self.create_modules()
|
||||
self.add_modules()
|
||||
self.setup_layout_constraints()
|
||||
self.route_power_ring(self.core_bbox)
|
||||
|
||||
if self.num_banks > 1:
|
||||
self.add_bank_select()
|
||||
self.add_power_ring(self.core_bbox)
|
||||
|
||||
# FIXME: Move this to the add modules function
|
||||
self.add_bank_select()
|
||||
|
||||
self.route_layout()
|
||||
|
||||
|
||||
|
||||
# Can remove the following, but it helps for debug!
|
||||
self.add_lvs_correspondence_points()
|
||||
|
|
@ -71,7 +76,7 @@ class bank(design.design):
|
|||
for i in range(self.word_size):
|
||||
self.add_pin("DATA[{0}]".format(i))
|
||||
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
|
||||
# the signals gated_*.
|
||||
|
|
@ -83,14 +88,14 @@ class bank(design.design):
|
|||
|
||||
def route_layout(self):
|
||||
""" Create routing amoung the modules """
|
||||
self.create_central_bus()
|
||||
self.route_central_bus()
|
||||
self.route_precharge_to_bitcell_array()
|
||||
self.route_sense_amp_to_trigate()
|
||||
self.route_tri_gate_out()
|
||||
self.route_wordline_driver()
|
||||
self.route_row_decoder()
|
||||
self.route_address()
|
||||
self.route_column_address_lines()
|
||||
self.route_msf_address()
|
||||
self.route_control_lines()
|
||||
self.add_control_pins()
|
||||
if self.num_banks > 1:
|
||||
|
|
@ -101,9 +106,12 @@ class bank(design.design):
|
|||
|
||||
def add_modules(self):
|
||||
""" Add modules. The order should not matter! """
|
||||
|
||||
# Above the bitcell array
|
||||
self.add_bitcell_array()
|
||||
self.add_precharge_array()
|
||||
|
||||
# Below the bitcell array
|
||||
if self.col_addr_size > 0:
|
||||
# The m2 width is because the 6T cell may have vias on the boundary edge for
|
||||
# overlapping when making the array
|
||||
|
|
@ -111,16 +119,17 @@ class bank(design.design):
|
|||
self.add_column_mux_array()
|
||||
else:
|
||||
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_write_driver_array()
|
||||
self.add_msf_data_in()
|
||||
self.add_tri_gate_array()
|
||||
|
||||
# To the left of the bitcell array
|
||||
self.add_row_decoder()
|
||||
self.add_wordline_driver()
|
||||
self.add_msf_address()
|
||||
self.add_column_decoder()
|
||||
|
||||
|
||||
|
||||
def compute_sizes(self):
|
||||
""" Computes the required sizes to create the bank """
|
||||
|
|
@ -143,28 +152,29 @@ class bank(design.design):
|
|||
self.num_control_lines = 6
|
||||
# The order of the control signals on the control bus:
|
||||
self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_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:
|
||||
self.control_signals = ["gated_"+str for str in self.input_control_signals]
|
||||
else:
|
||||
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:
|
||||
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:
|
||||
self.num_col_addr_lines = 0
|
||||
self.num_addr_lines = self.row_addr_size
|
||||
|
||||
# M1/M2 routing pitch is based on contacted pitch
|
||||
self.m1_pitch = contact.m1m2.height + max(self.m1_space,self.m2_space)
|
||||
self.m2_pitch = contact.m2m3.height + max(self.m2_space,self.m3_space)
|
||||
|
||||
# Overall central bus gap. It includes all the column mux lines,
|
||||
# control lines, and address flop to decoder lines
|
||||
# 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)
|
||||
self.start_of_left_central_bus = self.start_of_right_central_bus - self.m2_pitch*(self.num_addr_lines)
|
||||
# add a pitch on each side
|
||||
self.overall_central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 3)
|
||||
# The width of this bus is needed to place other modules (e.g. decoder)
|
||||
self.central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 1)
|
||||
|
||||
# The width of the bus below the decoder to route to the central bus
|
||||
self.addr_bus_height = self.m1_pitch * (self.addr_size + 1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -189,20 +199,15 @@ class bank(design.design):
|
|||
|
||||
|
||||
self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size,
|
||||
words_per_row=self.words_per_row)
|
||||
words_per_row=self.words_per_row)
|
||||
self.add_mod(self.sense_amp_array)
|
||||
|
||||
self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols,
|
||||
word_size=self.word_size)
|
||||
self.add_mod(self.write_driver_array)
|
||||
|
||||
self.decoder = self.mod_decoder(rows=self.num_rows)
|
||||
self.add_mod(self.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.row_decoder = self.mod_decoder(rows=self.num_rows)
|
||||
self.add_mod(self.row_decoder)
|
||||
|
||||
self.msf_data_in = self.mod_ms_flop_array(name="msf_data_in",
|
||||
columns=self.num_cols,
|
||||
|
|
@ -245,7 +250,7 @@ class bank(design.design):
|
|||
""" Adding Precharge """
|
||||
|
||||
# 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"]
|
||||
self.precharge_array_inst=self.add_inst(name="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 address flop and decoder are aligned in the x coord.
|
||||
|
||||
decoder_x_offset = self.decoder.width + self.overall_central_bus_width
|
||||
addr_x_offset = self.msf_address.height
|
||||
offset = vector(max(decoder_x_offset, addr_x_offset),
|
||||
self.decoder.predecoder_height)
|
||||
decoder_x_offset = self.row_decoder.width + self.central_bus_width
|
||||
offset = vector(decoder_x_offset,
|
||||
self.row_decoder.predecoder_height)
|
||||
self.row_decoder_inst=self.add_inst(name="row_decoder",
|
||||
mod=self.decoder,
|
||||
mod=self.row_decoder,
|
||||
offset=offset.scale(-1,-1))
|
||||
|
||||
temp = []
|
||||
|
|
@ -383,7 +387,7 @@ class bank(design.design):
|
|||
# The wordline driver is placed to the right of the main decoder width.
|
||||
# This means that it slightly overlaps with the hierarchical decoder,
|
||||
# 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",
|
||||
mod=self.wordline_driver,
|
||||
offset=vector(x_offset,0).scale(-1,-1))
|
||||
|
|
@ -398,73 +402,54 @@ class bank(design.design):
|
|||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
def add_msf_address(self):
|
||||
""" Adding address wires """
|
||||
|
||||
# A gap between the hierarchical decoder and addr flops
|
||||
gap = max(drc["pwell_to_nwell"], 2*self.m2_pitch)
|
||||
|
||||
# The address flops go below the hierarchical decoder
|
||||
decoder_x_offset = self.decoder.width + self.overall_central_bus_width
|
||||
addr_x_offset = self.msf_address.height + self.overall_central_bus_width
|
||||
# msf_address is not in the y-coord because it is rotated
|
||||
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
|
||||
def add_column_decoder_module(self):
|
||||
"""
|
||||
Create a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
# Place the col decoder aligned left to row decoder
|
||||
x_off = -(self.central_bus_width + self.row_decoder.width)
|
||||
y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + self.addr_bus_height)
|
||||
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
|
||||
mod=self.col_decoder,
|
||||
offset=vector(x_off,y_off).scale(-1,-1))
|
||||
offset=vector(x_off,y_off))
|
||||
|
||||
temp = []
|
||||
for i in range(self.col_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.extend(["vdd", "gnd"])
|
||||
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):
|
||||
""" 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
|
||||
yoffset = self.min_point + 2*self.supply_rail_pitch + self.m1_space
|
||||
self.bank_select_pos = vector(xoffset,yoffset)
|
||||
|
|
@ -492,7 +477,7 @@ class bank(design.design):
|
|||
for gated_name in self.control_signals:
|
||||
# Connect the inverter output to the central bus
|
||||
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_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=bus_pos,
|
||||
|
|
@ -506,34 +491,36 @@ class bank(design.design):
|
|||
|
||||
|
||||
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 column decoder (if there is one) or the tristate output
|
||||
#driver.
|
||||
# Leave room for the output below the tri gate.
|
||||
tri_gate_min_point = self.tri_gate_array_inst.ll().y - 3*self.m2_pitch
|
||||
addr_min_point = self.msf_address_inst.ll().y - 2*self.m2_pitch
|
||||
|
||||
if self.col_addr_size >1:
|
||||
self.decoder_min_point = self.col_decoder_inst.ll().y
|
||||
tri_gate_min_point = self.tri_gate_array_inst.by() - 3*self.m2_pitch
|
||||
row_decoder_min_point = self.row_decoder_inst.by()
|
||||
if self.col_addr_size > 0:
|
||||
col_decoder_min_point = self.col_decoder_inst.by()
|
||||
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:
|
||||
# The control gating logic is below the decoder
|
||||
# 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:
|
||||
# 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
|
||||
# Add a vdd and gnd power rail above the array
|
||||
self.max_point = self.precharge_array_inst.uy() + 3*self.m1_width
|
||||
# Create the core bbox for the power rings
|
||||
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]
|
||||
|
||||
# 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.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 create_central_bus(self):
|
||||
def route_central_bus(self):
|
||||
""" Create the address, supply, and control signal central bus lines. """
|
||||
|
||||
# Address lines in central line connection are 2*col_addr_size
|
||||
# number of connections for the column mux (for both signal and _bar) and row_addr_size (no _bar)
|
||||
# Overall central bus width. It includes all the column mux lines,
|
||||
# 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):
|
||||
x_offset = self.start_of_right_central_bus + i*self.m2_pitch
|
||||
self.central_line_xoffset[self.control_signals[i]]=x_offset + 0.5*self.m2_width
|
||||
x_offset = control_bus_x_offset + i*self.m2_pitch
|
||||
# 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
|
||||
self.add_rect(layer="metal2",
|
||||
offset=vector(x_offset, self.min_point),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
|
||||
# row address lines (to the left of the column mux or GND rail)
|
||||
# goes from 0 down to the bottom of the address flops
|
||||
# Row address lines (to left of col address lines)
|
||||
# goes from bottom of bitcell array down to the bottom of the column decoder/addresses
|
||||
for i in range(self.row_addr_size):
|
||||
x_offset = self.start_of_left_central_bus + i*self.m2_pitch
|
||||
name = "A[{}]".format(i)
|
||||
self.central_line_xoffset[name]=x_offset + 0.5*self.m2_width
|
||||
addr_idx = i + self.col_addr_size
|
||||
x_offset = address_bus_x_offset + i*self.m2_pitch
|
||||
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.
|
||||
self.add_label_pin(text=name,
|
||||
layer="metal2",
|
||||
offset=vector(x_offset, self.decoder_min_point),
|
||||
offset=vector(x_offset, self.addr_min_point),
|
||||
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)
|
||||
# goes from 0 down to the min point
|
||||
# Column mux lines if there is column mux
|
||||
# goes from bottom of bitcell array down to the bottom of the column decoder/addresses
|
||||
if self.col_addr_size>0:
|
||||
for i in range(2**self.col_addr_size):
|
||||
x_offset = self.start_of_left_central_bus + (i+self.row_addr_size)*self.m2_pitch
|
||||
name = "sel[{}]".format(i)
|
||||
self.central_line_xoffset[name]=x_offset + 0.5*self.m2_width
|
||||
for i in range(self.num_col_addr_lines):
|
||||
x_offset = address_bus_x_offset + (i+self.row_addr_size)*self.m2_pitch
|
||||
name = "sel[{}]".format(i) # One hot select signals
|
||||
# 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
|
||||
self.add_label_pin(text=name,
|
||||
layer="metal2",
|
||||
offset=vector(x_offset, self.decoder_min_point),
|
||||
offset=vector(x_offset, self.col_decoder_inst.by()),
|
||||
width=self.m2_width,
|
||||
height=-self.decoder_min_point)
|
||||
height=-self.col_decoder_inst.by())
|
||||
|
||||
|
||||
def route_precharge_to_bitcell_array(self):
|
||||
|
|
@ -758,10 +662,10 @@ class bank(design.design):
|
|||
def route_row_decoder(self):
|
||||
""" Routes the row decoder inputs and supplies """
|
||||
|
||||
|
||||
for i in range(self.row_addr_size):
|
||||
addr_idx = i + self.col_addr_size
|
||||
# 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
|
||||
if i < switchover_index:
|
||||
position_heights = i % 2
|
||||
|
|
@ -770,8 +674,10 @@ class bank(design.design):
|
|||
|
||||
# Connect the address rails to the decoder
|
||||
# Note that the decoder inputs are long vertical rails so spread out the connections vertically.
|
||||
decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() + vector(0,position_heights*self.bitcell.height+self.m2_pitch)
|
||||
rail_position = vector(self.central_line_xoffset["A[{}]".format(i)],decoder_in_position.y)
|
||||
decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() \
|
||||
+ vector(0,position_heights*self.bitcell.height+self.m2_pitch)
|
||||
rail_position = vector(self.bus_xoffset["A[{}]".format(addr_idx)],
|
||||
decoder_in_position.y)
|
||||
self.add_path("metal1",[decoder_in_position,rail_position])
|
||||
|
||||
decoder_in_via = decoder_in_position - vector(0,0.5*self.m2_width)
|
||||
|
|
@ -841,156 +747,101 @@ class bank(design.design):
|
|||
|
||||
# Connect the select lines to the column mux
|
||||
# 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)
|
||||
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_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=wire_pos,
|
||||
rotate=90)
|
||||
|
||||
# Take care of the column address decoder routing
|
||||
# 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
|
||||
# main address bus
|
||||
for i in range(2**self.col_addr_size):
|
||||
if self.col_addr_size == 1:
|
||||
|
||||
decode_out_pos = self.col_decoder_inst.get_pin("Zb").rc()
|
||||
selx_pos = vector(self.bus_xoffset["sel[0]"],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)
|
||||
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_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=selx_pos,
|
||||
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)
|
||||
x_offset = self.central_line_xoffset[name]
|
||||
decode_out_pos = self.col_decoder_inst.get_pin("out[{}]".format(i)).rc()
|
||||
selx_pos = vector(self.central_line_xoffset[name],decode_out_pos.y)
|
||||
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 the gnd rails, add contact to rail as well
|
||||
for gnd_pin in self.col_decoder_inst.get_pins("gnd"):
|
||||
left_rail_pos = vector(self.left_gnd_x_offset, gnd_pin.cy())
|
||||
self.add_path("metal1", [left_rail_pos, gnd_pin.rc()])
|
||||
# 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=left_rail_pos,
|
||||
size = (1,3),
|
||||
rotate=90)
|
||||
|
||||
# route the vdd rails
|
||||
for vdd_pin in self.col_decoder_inst.get_pins("vdd"):
|
||||
left_rail_pos = vector(self.left_vdd_x_center, vdd_pin.cy())
|
||||
self.add_path("metal1", [left_rail_pos, vdd_pin.rc()])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=left_rail_pos,
|
||||
size = (1,3),
|
||||
rotate=90)
|
||||
offset=addr_pos,
|
||||
rotate=90)
|
||||
|
||||
|
||||
# route the gnd rails, add contact to rail as well
|
||||
for gnd_pin in self.col_decoder_inst.get_pins("gnd"):
|
||||
left_rail_pos = vector(self.left_gnd_x_center, gnd_pin.cy())
|
||||
self.add_path("metal1", [left_rail_pos, gnd_pin.rc()])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=left_rail_pos,
|
||||
size = (1,3),
|
||||
rotate=90)
|
||||
|
||||
# route the vdd rails
|
||||
for vdd_pin in self.col_decoder_inst.get_pins("vdd"):
|
||||
left_rail_pos = vector(self.left_vdd_x_center, vdd_pin.cy())
|
||||
self.add_path("metal1", [left_rail_pos, vdd_pin.rc()])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=left_rail_pos,
|
||||
size = (1,3),
|
||||
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"),
|
||||
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):
|
||||
def route_address(self):
|
||||
""" 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):
|
||||
msf_din_pins = self.msf_address_inst.get_pins("din[{}]".format(i))
|
||||
for pin in msf_din_pins:
|
||||
if pin.layer=="metal3":
|
||||
msf_din_pos=pin.ll()
|
||||
break
|
||||
address_pos = vector(self.left_gnd_x_offset, msf_din_pos.y)
|
||||
self.add_layout_pin(text="ADDR[{}]".format(i),
|
||||
layer="metal3",
|
||||
offset=address_pos,
|
||||
width=msf_din_pos.x - address_pos.x)
|
||||
|
||||
|
||||
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)
|
||||
name = "A[{}]".format(i)
|
||||
address_y_offset = self.addr_min_point + (i+1)*self.m1_pitch
|
||||
pin_pos = vector(self.left_gnd_x_offset, address_y_offset)
|
||||
if name in self.bus_xoffset:
|
||||
rail_pos = vector(self.bus_xoffset[name],pin_pos.y)
|
||||
else:
|
||||
# Route to right of col decoder if it's not part of central bus
|
||||
rail_pos = vector(self.col_decoder_inst.rx(),pin_pos.y)
|
||||
self.add_layout_pin_center_segment(text=name,
|
||||
layer="metal1",
|
||||
start=pin_pos,
|
||||
end=rail_pos)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
|
||||
# Connect address FF gnd
|
||||
for gnd_pin in self.msf_address_inst.get_pins("gnd"):
|
||||
if gnd_pin.layer != "metal2":
|
||||
continue
|
||||
gnd_via = gnd_pin.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):
|
||||
""" 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()))
|
||||
|
||||
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_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=control_pos,
|
||||
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
|
||||
control_signal = self.prefix+"clk_buf"
|
||||
pin_pos = self.wordline_driver_inst.get_pin("en").uc()
|
||||
mid_pos = pin_pos + vector(0,self.m1_pitch)
|
||||
control_x_offset = self.central_line_xoffset[control_signal]
|
||||
control_x_offset = self.bus_xoffset[control_signal]
|
||||
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
|
||||
control_via_pos = vector(control_x_offset, mid_pos.y)
|
||||
|
|
@ -1162,18 +1003,19 @@ class bank(design.design):
|
|||
""" Add the control signal input pins """
|
||||
|
||||
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:
|
||||
# it's not an input pin if we have multiple banks
|
||||
self.add_label_pin(text=ctrl,
|
||||
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,
|
||||
height=self.height)
|
||||
else:
|
||||
self.add_layout_pin(text=ctrl,
|
||||
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,
|
||||
height=self.height)
|
||||
|
||||
|
|
@ -1206,9 +1048,9 @@ class bank(design.design):
|
|||
|
||||
def analytical_delay(self, slew, load):
|
||||
""" 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())
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ class bitcell_array(design.design):
|
|||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.add_layout_pins()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from pinv import pinv
|
|||
from pnand2 import pnand2
|
||||
from pnand3 import pnand3
|
||||
from pnor2 import pnor2
|
||||
from pinvbuf import pinvbuf
|
||||
import math
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
|
@ -30,7 +31,7 @@ class control_logic(design.design):
|
|||
self.create_modules()
|
||||
self.setup_layout_offsets()
|
||||
self.add_modules()
|
||||
self.add_routing()
|
||||
#self.add_routing()
|
||||
|
||||
def create_modules(self):
|
||||
""" add all the required modules """
|
||||
|
|
@ -48,23 +49,18 @@ class control_logic(design.design):
|
|||
self.add_mod(self.nor2)
|
||||
|
||||
# Special gates: inverters for buffering
|
||||
self.clkbuf = pinvbuf(4,16)
|
||||
self.add_mod(self.clkbuf)
|
||||
self.inv = self.inv1 = pinv(1)
|
||||
self.add_mod(self.inv1)
|
||||
self.inv2 = pinv(2)
|
||||
self.add_mod(self.inv2)
|
||||
self.inv4 = pinv(4)
|
||||
self.add_mod(self.inv4)
|
||||
self.inv8 = pinv(8)
|
||||
self.add_mod(self.inv8)
|
||||
self.inv16 = pinv(16)
|
||||
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)
|
||||
# self.inv2 = pinv(2)
|
||||
# self.add_mod(self.inv2)
|
||||
# self.inv4 = pinv(4)
|
||||
# self.add_mod(self.inv4)
|
||||
# self.inv8 = pinv(8)
|
||||
# self.add_mod(self.inv8)
|
||||
# self.inv16 = pinv(16)
|
||||
# self.add_mod(self.inv16)
|
||||
|
||||
c = reload(__import__(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
|
||||
self.rail_1_start_x = 0
|
||||
self.num_rails_1 = 8
|
||||
self.rail_1_names = ["clk_buf", "gnd", "oe_bar", "cs", "we", "vdd", "oe", "clk_bar"]
|
||||
self.num_rails_1 = 7
|
||||
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.rail_1_x_offsets = {}
|
||||
|
||||
|
|
@ -103,18 +99,21 @@ class control_logic(design.design):
|
|||
|
||||
def add_modules(self):
|
||||
""" Place all the modules """
|
||||
self.add_control_flops()
|
||||
self.add_clk_buffer(0)
|
||||
self.add_1st_row(0)
|
||||
self.add_2nd_row(self.inv1.height)
|
||||
self.add_3rd_row(2*self.inv1.height)
|
||||
self.add_control_routing()
|
||||
self.add_rbl(0)
|
||||
|
||||
self.add_clk_buffer(row=0) # 0 and 1st row
|
||||
self.add_oe_row(row=2)
|
||||
self.add_sen_row(row=3)
|
||||
self.add_we_row(row=4)
|
||||
|
||||
#self.add_control_routing()
|
||||
|
||||
self.add_rbl(row=5)
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
@ -122,7 +121,7 @@ class control_logic(design.design):
|
|||
|
||||
def add_routing(self):
|
||||
""" Routing between modules """
|
||||
self.add_clk_routing()
|
||||
#self.add_clk_routing()
|
||||
self.add_trien_routing()
|
||||
self.add_rblk_routing()
|
||||
self.add_wen_routing()
|
||||
|
|
@ -130,116 +129,117 @@ class control_logic(design.design):
|
|||
self.add_output_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 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)
|
||||
y_off = row * self.inv1.height
|
||||
|
||||
self.replica_bitline_offset = vector(rows_end_x , y_off)
|
||||
# Add the RBL above the rows
|
||||
# Add to the right of the control rows and routing channel
|
||||
self.replica_bitline_offset = vector(0, y_off)
|
||||
self.rbl=self.add_inst(name="replica_bitline",
|
||||
mod=self.replica_bitline,
|
||||
offset=self.replica_bitline_offset,
|
||||
mirror="MX",
|
||||
rotate=90)
|
||||
offset=self.replica_bitline_offset)
|
||||
self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"])
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the input/output layout pins. """
|
||||
|
||||
# Top to bottom: CS WE OE signal groups
|
||||
pin_set = ["oeb","csb","web"]
|
||||
for (i,pin_name) in zip(range(3),pin_set):
|
||||
subpin_name="din[{}]".format(i)
|
||||
pins=self.msf_inst.get_pins(subpin_name)
|
||||
for pin in pins:
|
||||
if pin.layer=="metal3":
|
||||
self.add_layout_pin(text=pin_name,
|
||||
layer="metal3",
|
||||
offset=pin.ll(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
# # Top to bottom: CS WE OE signal groups
|
||||
# pin_set = ["oeb","csb","web"]
|
||||
# for (i,pin_name) in zip(range(3),pin_set):
|
||||
# subpin_name="din[{}]".format(i)
|
||||
# pins=self.msf_inst.get_pins(subpin_name)
|
||||
# for pin in pins:
|
||||
# if pin.layer=="metal3":
|
||||
# self.add_layout_pin(text=pin_name,
|
||||
# layer="metal3",
|
||||
# offset=pin.ll(),
|
||||
# width=pin.width(),
|
||||
# height=pin.height())
|
||||
|
||||
pin=self.clk_inv1.get_pin("A")
|
||||
pin=self.clkbuf.get_pin("A")
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal1",
|
||||
offset=pin.ll().scale(0,1),
|
||||
width=pin.rx(),
|
||||
height=pin.height())
|
||||
|
||||
pin=self.clk_inv1.get_pin("gnd")
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=pin.ll(),
|
||||
width=self.width)
|
||||
# pin=self.clkbuf.get_pin("gnd")
|
||||
# self.add_layout_pin(text="gnd",
|
||||
# layer="metal1",
|
||||
# offset=pin.ll(),
|
||||
# width=self.width)
|
||||
|
||||
pin=self.clk_inv1.get_pin("vdd")
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=pin.ll(),
|
||||
width=self.width)
|
||||
# pin=self.clkbuf.get_pin("vdd")
|
||||
# self.add_layout_pin(text="vdd",
|
||||
# layer="metal1",
|
||||
# offset=pin.ll(),
|
||||
# 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 """
|
||||
# 4 stage clock buffer
|
||||
self.clk_inv1_offset = vector(0, 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"])
|
||||
y_off = row*self.inv1.height
|
||||
if row % 2:
|
||||
y_off += self.clkbuf.height
|
||||
mirror="MX"
|
||||
else:
|
||||
mirror="R0"
|
||||
|
||||
# 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()])
|
||||
clkbuf_offset = vector(0,y_off)
|
||||
self.clkbuf_inst = self.add_inst(name="clkbuf",
|
||||
mod=self.clkbuf,
|
||||
offset=clkbuf_offset)
|
||||
|
||||
self.connect_inst(["clk","clk_bar","clk","vdd","gnd"])
|
||||
|
||||
# # 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()])
|
||||
|
||||
# This is the first rail offset
|
||||
self.rail_1_start_x = max(self.msf_offset.x + self.msf_control.height,self.clk_buf_offset.x+self.inv16.width) + self.m2_pitch
|
||||
|
||||
|
||||
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
|
||||
self.rblk_bar_offset = vector(x_off, y_off)
|
||||
self.rblk_bar=self.add_inst(name="nand3_rblk_bar",
|
||||
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"])
|
||||
x_off += self.nand3.width
|
||||
|
||||
|
|
@ -247,36 +247,21 @@ class control_logic(design.design):
|
|||
self.rblk_offset = vector(x_off, y_off)
|
||||
self.rblk=self.add_inst(name="inv_rblk",
|
||||
mod=self.inv1,
|
||||
offset=self.rblk_offset)
|
||||
offset=self.rblk_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"])
|
||||
#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):
|
||||
# start after first rails
|
||||
x_off = self.rail_1_start_x + self.overall_rail_1_gap
|
||||
y_off += self.inv1.height
|
||||
|
||||
# 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="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
|
||||
def add_sen_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"
|
||||
|
||||
# BUFFER INVERTERS FOR 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",
|
||||
mod=self.inv1,
|
||||
offset=self.s_en_offset,
|
||||
mirror="XY")
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
|
||||
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",
|
||||
mod=self.inv1,
|
||||
offset=self.pre_s_en_bar_offset,
|
||||
mirror="XY")
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
|
||||
#x_off += self.inv1.width
|
||||
|
||||
self.row_sen_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"
|
||||
|
||||
self.row_2_end_x = x_off
|
||||
# input: oe output: oe_bar
|
||||
self.oe_inv_offset = vector(x_off, y_off)
|
||||
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
|
||||
|
||||
|
||||
def add_3rd_row(self, y_off):
|
||||
# start after first rails
|
||||
x_off = self.rail_1_start_x + self.overall_rail_1_gap
|
||||
# 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
|
||||
|
||||
# This prevents some M2 outputs from overlapping (hack)
|
||||
x_off += self.inv1.width
|
||||
|
||||
|
||||
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
|
||||
self.w_en_bar_offset = vector(x_off, y_off)
|
||||
self.w_en_bar=self.add_inst(name="nand3_w_en_bar",
|
||||
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"])
|
||||
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=self.add_inst(name="inv_pre_w_en",
|
||||
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"])
|
||||
x_off += self.inv1.width
|
||||
|
||||
|
|
@ -328,71 +360,65 @@ class control_logic(design.design):
|
|||
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",
|
||||
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"])
|
||||
x_off += self.inv1.width
|
||||
|
||||
self.w_en_offset = vector(x_off, y_off)
|
||||
self.w_en=self.add_inst(name="inv_w_en2",
|
||||
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"])
|
||||
#x_off += self.inv1.width
|
||||
|
||||
self.row_3_end_x = x_off
|
||||
self.row_we_end_x = x_off
|
||||
|
||||
def add_control_routing(self):
|
||||
""" Route the vertical rails for internal control signals """
|
||||
# def add_control_routing(self):
|
||||
# """ 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):
|
||||
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"]:
|
||||
self.add_layout_pin(text=self.rail_1_names[i],
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=control_rail_height)
|
||||
else:
|
||||
# just for LVS correspondence...
|
||||
self.add_label_pin(text=self.rail_1_names[i],
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=control_rail_height)
|
||||
self.rail_1_x_offsets[self.rail_1_names[i]]=offset.x + 0.5*drc["minwidth_metal2"] # center offset
|
||||
# for i in range(self.num_rails_1):
|
||||
# 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"]:
|
||||
# self.add_layout_pin(text=self.rail_1_names[i],
|
||||
# layer="metal2",
|
||||
# offset=offset,
|
||||
# width=drc["minwidth_metal2"],
|
||||
# height=control_rail_height)
|
||||
# else:
|
||||
# # just for LVS correspondence...
|
||||
# self.add_label_pin(text=self.rail_1_names[i],
|
||||
# layer="metal2",
|
||||
# offset=offset,
|
||||
# width=drc["minwidth_metal2"],
|
||||
# height=control_rail_height)
|
||||
# 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)
|
||||
|
||||
# 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])
|
||||
|
||||
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])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def add_rblk_routing(self):
|
||||
|
|
@ -482,8 +508,13 @@ class control_logic(design.design):
|
|||
|
||||
|
||||
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,"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,"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()])
|
||||
|
||||
def add_clk_routing(self):
|
||||
""" Route the clk and clk_bar signal internally """
|
||||
# def add_clk_routing(self):
|
||||
# """ Route the clk and clk_bar signal internally """
|
||||
|
||||
# clk_buf
|
||||
clk_buf_pos = self.clk_buf.get_pin("Z").rc()
|
||||
clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], clk_buf_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[clk_buf_pos, clk_buf_rail_position])
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=clk_buf_rail_position,
|
||||
rotate=90)
|
||||
# # clk_buf
|
||||
# clk_buf_pos = self.clk_buf.get_pin("Z").rc()
|
||||
# clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], clk_buf_pos.y)
|
||||
# self.add_wire(("metal1","via1","metal2"),[clk_buf_pos, clk_buf_rail_position])
|
||||
# self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
# offset=clk_buf_rail_position,
|
||||
# rotate=90)
|
||||
|
||||
# clk_bar, routes over the clock buffer vdd rail
|
||||
clk_pin = self.clk_bar.get_pin("Z")
|
||||
vdd_pin = self.clk_bar.get_pin("vdd")
|
||||
# move the output pin up to metal2
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=clk_pin.rc(),
|
||||
rotate=90)
|
||||
# route to a position over the supply rail
|
||||
in_pos = vector(clk_pin.rx(), vdd_pin.cy())
|
||||
self.add_path("metal2",[clk_pin.rc(), in_pos])
|
||||
# connect that position to the control bus
|
||||
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_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=in_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=rail_pos,
|
||||
rotate=90)
|
||||
# # clk_bar, routes over the clock buffer vdd rail
|
||||
# clk_pin = self.clk_bar.get_pin("Z")
|
||||
# vdd_pin = self.clk_bar.get_pin("vdd")
|
||||
# # move the output pin up to metal2
|
||||
# self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
# offset=clk_pin.rc(),
|
||||
# rotate=90)
|
||||
# # route to a position over the supply rail
|
||||
# in_pos = vector(clk_pin.rx(), vdd_pin.cy())
|
||||
# self.add_path("metal2",[clk_pin.rc(), in_pos])
|
||||
# # connect that position to the control bus
|
||||
# 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_via_center(layers=("metal2","via2","metal3"),
|
||||
# offset=in_pos,
|
||||
# rotate=90)
|
||||
# self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
# offset=rail_pos,
|
||||
# rotate=90)
|
||||
|
||||
# clk_buf to msf control flops
|
||||
msf_clk_pos = self.msf_inst.get_pin("clk").bc()
|
||||
mid1 = msf_clk_pos - vector(0,self.m2_pitch)
|
||||
clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], mid1.y)
|
||||
# route on M2 to allow vdd connection
|
||||
self.add_wire(("metal2","via1","metal1"),[msf_clk_pos, mid1, clk_buf_rail_position])
|
||||
# # clk_buf to msf control flops
|
||||
# msf_clk_pos = self.msf_inst.get_pin("clk").bc()
|
||||
# mid1 = msf_clk_pos - vector(0,self.m2_pitch)
|
||||
# clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], mid1.y)
|
||||
# # route on M2 to allow vdd connection
|
||||
# self.add_wire(("metal2","via1","metal1"),[msf_clk_pos, mid1, clk_buf_rail_position])
|
||||
|
||||
def connect_right_pin_to_output_pin(self, inst, pin_name, out_name):
|
||||
""" Create an output pin on the bottom side from the pin of a given instance. """
|
||||
|
|
@ -580,7 +611,7 @@ class control_logic(design.design):
|
|||
well_width = drc["minwidth_well"]
|
||||
|
||||
# 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)
|
||||
self.add_path("metal1",[start_offset,row1_gnd_end_offset])
|
||||
rail_position = vector(self.rail_1_x_offsets["gnd"], start_offset.y)
|
||||
|
|
@ -597,7 +628,7 @@ class control_logic(design.design):
|
|||
height=well_width)
|
||||
|
||||
# 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)
|
||||
self.add_path("metal1",[start_offset,row1_vdd_end_offset])
|
||||
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
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
pin=self.clk_inv1.get_pin("Z")
|
||||
self.add_label_pin(text="clk1_bar",
|
||||
layer="metal1",
|
||||
offset=pin.ll(),
|
||||
height=pin.height(),
|
||||
width=pin.width())
|
||||
# pin=self.clk_inv1.get_pin("Z")
|
||||
# self.add_label_pin(text="clk1_bar",
|
||||
# layer="metal1",
|
||||
# offset=pin.ll(),
|
||||
# height=pin.height(),
|
||||
# width=pin.width())
|
||||
|
||||
pin=self.clk_inv2.get_pin("Z")
|
||||
self.add_label_pin(text="clk2",
|
||||
layer="metal1",
|
||||
offset=pin.ll(),
|
||||
height=pin.height(),
|
||||
width=pin.width())
|
||||
# pin=self.clk_inv2.get_pin("Z")
|
||||
# self.add_label_pin(text="clk2",
|
||||
# layer="metal1",
|
||||
# offset=pin.ll(),
|
||||
# height=pin.height(),
|
||||
# width=pin.width())
|
||||
|
||||
pin=self.rbl.get_pin("out")
|
||||
self.add_label_pin(text="out",
|
||||
|
|
@ -688,4 +719,4 @@ class control_logic(design.design):
|
|||
height=pin.height(),
|
||||
width=pin.width())
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class delay_chain(design.design):
|
|||
|
||||
self.add_pins()
|
||||
self.create_module()
|
||||
self.route_inv()
|
||||
self.route_inverters()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
|
@ -48,82 +48,69 @@ class delay_chain(design.design):
|
|||
def create_module(self):
|
||||
""" Add the inverter logical module """
|
||||
|
||||
self.create_inv_list()
|
||||
|
||||
self.inv = pinv(route_output=False)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
# half chain length is the width of the layout
|
||||
# invs are stacked into 2 levels so input/output are close
|
||||
# extra metal is for the gnd connection U
|
||||
self.width = self.num_top_half * self.inv.width + 2*drc["metal1_to_metal1"] + 0.5*drc["minwidth_metal1"]
|
||||
self.height = 2 * self.inv.height
|
||||
self.height = len(self.fanout_list)*self.inv.height
|
||||
self.width = (max(self.fanout_list)+1) * self.inv.width
|
||||
|
||||
self.add_inv_list()
|
||||
self.add_inverters()
|
||||
|
||||
def create_inv_list(self):
|
||||
"""
|
||||
Generate a list of inverters. Each inverter has a stage
|
||||
number and a flag indicating if it is a dummy load. This is
|
||||
the order that they will get placed too.
|
||||
"""
|
||||
# First stage is always 0 and is not a dummy load
|
||||
self.inv_list=[[0,False]]
|
||||
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
|
||||
for i in range(fanout_size-1):
|
||||
# Add the dummy loads
|
||||
self.inv_list.append([stage_num+1, True])
|
||||
|
||||
# Add the gate to drive the next stage
|
||||
self.inv_list.append([stage_num+1, False])
|
||||
|
||||
def add_inv_list(self):
|
||||
def add_inverters(self):
|
||||
""" Add the inverters and connect them based on the stage list """
|
||||
dummy_load_counter = 1
|
||||
self.inv_inst_list = []
|
||||
for i in range(self.num_inverters):
|
||||
# First place the gates
|
||||
if i < self.num_top_half:
|
||||
# add top level that is upside down
|
||||
inv_offset = vector(i * self.inv.width, 2 * self.inv.height)
|
||||
inv_mirror="MX"
|
||||
self.driver_inst_list = []
|
||||
self.rightest_load_inst = {}
|
||||
self.load_inst_map = {}
|
||||
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
|
||||
if stage_num % 2:
|
||||
inv_mirror = "MX"
|
||||
inv_offset = vector(0, (stage_num+1)* self.inv.height)
|
||||
else:
|
||||
# add bottom level from right to left
|
||||
inv_offset = vector((self.num_inverters - i) * self.inv.width, 0)
|
||||
inv_mirror="MY"
|
||||
|
||||
cur_inv=self.add_inst(name="dinv{}".format(i),
|
||||
inv_mirror = "R0"
|
||||
inv_offset = vector(0, stage_num * self.inv.height)
|
||||
|
||||
# Add the inverter
|
||||
cur_driver=self.add_inst(name="dinv{}".format(stage_num),
|
||||
mod=self.inv,
|
||||
offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
# keep track of the inverter instances so we can use them to get the pins
|
||||
self.inv_inst_list.append(cur_inv)
|
||||
self.driver_inst_list.append(cur_driver)
|
||||
|
||||
# Second connect them logically
|
||||
cur_stage = self.inv_list[i][0]
|
||||
next_stage = self.inv_list[i][0]+1
|
||||
if i == 0:
|
||||
input = "in"
|
||||
|
||||
# Hook up the driver
|
||||
if stage_num+1==len(self.fanout_list):
|
||||
stageout_name = "out"
|
||||
else:
|
||||
input = "s{}".format(cur_stage)
|
||||
if i == self.num_inverters-1:
|
||||
output = "out"
|
||||
else:
|
||||
output = "s{}".format(next_stage)
|
||||
|
||||
# if the gate is a dummy load don't connect the output
|
||||
# else reset the counter
|
||||
if self.inv_list[i][1]:
|
||||
output = output+"n{0}".format(dummy_load_counter)
|
||||
dummy_load_counter += 1
|
||||
stageout_name = "dout_{}".format(stage_num+1)
|
||||
if stage_num == 0:
|
||||
stagein_name = "in"
|
||||
else:
|
||||
dummy_load_counter = 1
|
||||
|
||||
self.connect_inst(args=[input, output, "vdd", "gnd"])
|
||||
|
||||
if i != 0:
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=cur_inv.get_pin("A").center())
|
||||
stagein_name = "dout_{}".format(stage_num)
|
||||
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
|
||||
|
||||
# Now add the dummy loads to the right
|
||||
self.load_inst_map[cur_driver]=[]
|
||||
for i in range(fanout_size):
|
||||
inv_offset += vector(self.inv.width,0)
|
||||
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num,i),
|
||||
mod=self.inv,
|
||||
offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
# Fanout stage is always driven by driver and output is disconnected
|
||||
disconnect_name = "n_{0}_{1}".format(stage_num,i)
|
||||
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
|
||||
|
||||
# Keep track of all the loads to connect their inputs as a load
|
||||
self.load_inst_map[cur_driver].append(cur_load)
|
||||
else:
|
||||
# Keep track of the last one so we can add the the wire later
|
||||
self.rightest_load_inst[cur_driver]=cur_load
|
||||
|
||||
def add_route(self, pin1, pin2):
|
||||
""" This guarantees that we route from the top to bottom row correctly. """
|
||||
pin1_pos = pin1.center()
|
||||
|
|
@ -135,79 +122,88 @@ class delay_chain(design.design):
|
|||
# Written this way to guarantee it goes right first if we are switching rows
|
||||
self.add_path("metal2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos])
|
||||
|
||||
def route_inv(self):
|
||||
def route_inverters(self):
|
||||
""" Add metal routing for each of the fanout stages """
|
||||
start_inv = end_inv = 0
|
||||
for fanout in self.fanout_list:
|
||||
# end inv number depends on the fan out number
|
||||
end_inv = start_inv + fanout
|
||||
start_inv_inst = self.inv_inst_list[start_inv]
|
||||
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=start_inv_inst.get_pin("Z").center()),
|
||||
|
||||
# route from output to first load
|
||||
start_inv_pin = start_inv_inst.get_pin("Z")
|
||||
load_inst = self.inv_inst_list[start_inv+1]
|
||||
load_pin = load_inst.get_pin("A")
|
||||
self.add_route(start_inv_pin, load_pin)
|
||||
|
||||
next_inv = start_inv+2
|
||||
while next_inv <= end_inv:
|
||||
prev_load_inst = self.inv_inst_list[next_inv-1]
|
||||
prev_load_pin = prev_load_inst.get_pin("A")
|
||||
load_inst = self.inv_inst_list[next_inv]
|
||||
load_pin = load_inst.get_pin("A")
|
||||
self.add_route(prev_load_pin, load_pin)
|
||||
next_inv += 1
|
||||
# set the start of next one after current end
|
||||
start_inv = end_inv
|
||||
for i in range(len(self.driver_inst_list)):
|
||||
inv = self.driver_inst_list[i]
|
||||
for load in self.load_inst_map[inv]:
|
||||
# Drop a via on each A pin
|
||||
a_pin = load.get_pin("A")
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=a_pin.center(),
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=a_pin.center(),
|
||||
rotate=90)
|
||||
|
||||
# Route an M3 horizontal wire to the furthest
|
||||
z_pin = inv.get_pin("Z")
|
||||
a_pin = inv.get_pin("A")
|
||||
a_max = self.rightest_load_inst[inv].get_pin("A")
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=a_pin.center(),
|
||||
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()])
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
|
||||
the top end with no input/output to obstruct. """
|
||||
vdd_pin = self.inv.get_pin("vdd")
|
||||
gnd_pin = self.inv.get_pin("gnd")
|
||||
for i in range(3):
|
||||
(offset,y_dir)=self.get_gate_offset(0, self.inv.height, i)
|
||||
rail_width = self.num_top_half * self.inv.width
|
||||
if i % 2:
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=offset + vdd_pin.ll().scale(1,y_dir),
|
||||
width=rail_width,
|
||||
height=drc["minwidth_metal1"])
|
||||
else:
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=offset + gnd_pin.ll().scale(1,y_dir),
|
||||
width=rail_width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
# Use the right most parts of the gnd rails and add a U connector
|
||||
# We still have the two gnd pins, but it is an either-or connect
|
||||
gnd_pins = self.get_pins("gnd")
|
||||
gnd_start = gnd_pins[0].rc()
|
||||
gnd_mid1 = gnd_start + vector(2*drc["metal1_to_metal1"],0)
|
||||
gnd_end = gnd_pins[1].rc()
|
||||
gnd_mid2 = gnd_end + vector(2*drc["metal1_to_metal1"],0)
|
||||
#self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
|
||||
self.add_path("metal1", [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
|
||||
|
||||
for driver in self.driver_inst_list:
|
||||
vdd_pin = driver.get_pin("vdd")
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_pin.ll(),
|
||||
width=self.width,
|
||||
height=vdd_pin.height())
|
||||
gnd_pin = driver.get_pin("gnd")
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_pin.ll(),
|
||||
width=self.width,
|
||||
height=gnd_pin.height())
|
||||
|
||||
# input is A pin of first inverter
|
||||
a_pin = self.inv_inst_list[0].get_pin("A")
|
||||
a_pin = self.driver_inst_list[0].get_pin("A")
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=a_pin.center(),
|
||||
rotate=90)
|
||||
self.add_layout_pin(text="in",
|
||||
layer="metal1",
|
||||
offset=a_pin.ll(),
|
||||
layer="metal2",
|
||||
offset=a_pin.ll().scale(1,0),
|
||||
width=a_pin.width(),
|
||||
height=a_pin.height())
|
||||
height=a_pin.cy())
|
||||
|
||||
|
||||
|
||||
# output is Z pin of last inverter
|
||||
z_pin = self.inv_inst_list[-1].get_pin("Z")
|
||||
# output is A pin of last load inverter
|
||||
last_driver_inst = self.driver_inst_list[-1]
|
||||
a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A")
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=a_pin.center(),
|
||||
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",
|
||||
layer="metal1",
|
||||
offset=z_pin.ll().scale(0,1),
|
||||
width=z_pin.lx())
|
||||
layer="metal2",
|
||||
offset=mid_point.scale(1,0))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ from tech import drc
|
|||
from math import log
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
import dff_buf
|
||||
|
||||
class dff_array(design.design):
|
||||
"""
|
||||
|
|
@ -21,7 +20,9 @@ class dff_array(design.design):
|
|||
design.design.__init__(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.width = self.columns * self.dff.width
|
||||
|
|
@ -42,7 +43,6 @@ class dff_array(design.design):
|
|||
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")
|
||||
|
|
@ -64,7 +64,6 @@ class dff_array(design.design):
|
|||
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"])
|
||||
|
|
@ -89,15 +88,6 @@ class dff_array(design.design):
|
|||
|
||||
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):
|
||||
|
||||
|
|
@ -138,14 +128,6 @@ class dff_array(design.design):
|
|||
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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -31,7 +31,7 @@ class hierarchical_decoder(design.design):
|
|||
self.rows = rows
|
||||
self.num_inputs = int(math.log(self.rows, 2))
|
||||
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||
|
||||
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
|
@ -51,10 +51,15 @@ class hierarchical_decoder(design.design):
|
|||
self.add_mod(self.nand2)
|
||||
self.nand3 = pnand3()
|
||||
self.add_mod(self.nand3)
|
||||
|
||||
self.add_decoders()
|
||||
|
||||
# CREATION OF PRE-DECODER
|
||||
def add_decoders(self):
|
||||
""" Create the decoders based on the number of pre-decodes """
|
||||
# FIXME: Only add these if needed?
|
||||
self.pre2_4 = pre2x4()
|
||||
self.add_mod(self.pre2_4)
|
||||
|
||||
self.pre3_8 = pre3x8()
|
||||
self.add_mod(self.pre3_8)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell_height = self.mod_bitcell.height
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
for k in range(self.number_of_inputs):
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ class options(optparse.Values):
|
|||
# This is the name of the technology.
|
||||
tech_name = ""
|
||||
# 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 = "/Users/{}/openram_temp/".format(getpass.getuser())
|
||||
#openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid())
|
||||
openram_temp = "/Users/{}/openram_temp/".format(getpass.getuser())
|
||||
# This is the verbosity level to control debug information. 0 is none, 1
|
||||
# is minimal, etc.
|
||||
debug_level = 0
|
||||
|
|
@ -59,7 +59,7 @@ class options(optparse.Values):
|
|||
ms_flop = "ms_flop"
|
||||
ms_flop_array = "ms_flop_array"
|
||||
dff = "dff"
|
||||
dff_array = "dff_array"
|
||||
dff_array = "dff_buf_array"
|
||||
control_logic = "control_logic"
|
||||
bitcell_array = "bitcell_array"
|
||||
sense_amp = "sense_amp"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -22,8 +22,8 @@ class sram(design.design):
|
|||
c = reload(__import__(OPTS.control_logic))
|
||||
self.mod_control_logic = getattr(c, OPTS.control_logic)
|
||||
|
||||
c = reload(__import__(OPTS.ms_flop_array))
|
||||
self.mod_ms_flop_array = getattr(c, OPTS.ms_flop_array)
|
||||
c = reload(__import__(OPTS.dff_array))
|
||||
self.mod_dff_array = getattr(c, OPTS.dff_array)
|
||||
|
||||
c = reload(__import__(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")
|
||||
|
||||
# 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.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("gnd","GROUND")
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ class sram(design.design):
|
|||
""" Route the shared signals for two and four bank configurations. """
|
||||
|
||||
# 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)
|
||||
|
||||
# connect the control logic to the control bus
|
||||
|
|
@ -374,7 +374,7 @@ class sram(design.design):
|
|||
length=self.vertical_bus_height,
|
||||
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",
|
||||
pitch=self.m2_pitch,
|
||||
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.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)
|
||||
self.bank = bank(word_size=self.word_size,
|
||||
num_words=self.num_words_per_bank,
|
||||
|
|
@ -805,14 +810,13 @@ class sram(design.design):
|
|||
name="bank")
|
||||
self.add_mod(self.bank)
|
||||
|
||||
# Conditionally create the
|
||||
# Create bank decoder
|
||||
if(self.num_banks > 1):
|
||||
self.create_multi_bank_modules()
|
||||
|
||||
self.bank_count = 0
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
@ -900,13 +904,32 @@ class sram(design.design):
|
|||
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):
|
||||
""" Add and place control logic """
|
||||
self.control_logic_inst=self.add_inst(name="control",
|
||||
mod=self.control_logic,
|
||||
offset=position,
|
||||
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):
|
||||
|
|
@ -938,11 +961,14 @@ class sram(design.design):
|
|||
# are not recomputed using instance placement. So, place the control logic such that it aligns
|
||||
# with the top of the SRAM.
|
||||
control_gap = 2*self.m3_width
|
||||
pos = vector(-control_gap,
|
||||
self.bank.height-self.control_logic.width)
|
||||
self.add_control_logic(position=pos,
|
||||
rotate=90)
|
||||
control_pos = vector(-control_gap,
|
||||
self.bank.height-self.control_logic.width)
|
||||
self.add_control_logic(position=control_pos, 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.height = self.bank.height
|
||||
|
||||
|
|
@ -955,7 +981,7 @@ class sram(design.design):
|
|||
self.copy_layout_pin(self.bank_inst, "DATA[{}]".format(i))
|
||||
|
||||
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"]):
|
||||
self.copy_layout_pin(self.control_logic_inst, old, new)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -34,9 +34,9 @@ class single_bank_test(openram_test):
|
|||
self.local_check(a)
|
||||
|
||||
# Eight way has a short circuit of one column mux select to gnd rail
|
||||
# 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")
|
||||
# self.local_check(a)
|
||||
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")
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
|
|
|||
Loading…
Reference in New Issue