mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'tech_migration' into dev
This commit is contained in:
commit
9df99beb28
|
|
@ -136,7 +136,7 @@ class geometry:
|
|||
def cy(self):
|
||||
""" Return the center y """
|
||||
return 0.5 * (self.boundary[0].y + self.boundary[1].y)
|
||||
|
||||
|
||||
|
||||
class instance(geometry):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -316,16 +316,17 @@ class layout():
|
|||
You can optionally rename the pin to a new name.
|
||||
"""
|
||||
for pin_name in self.pin_map.keys():
|
||||
self.copy_layout_pin(instance, pin_name, prefix+pin_name)
|
||||
self.copy_layout_pin(instance, pin_name, prefix + pin_name)
|
||||
|
||||
def add_layout_pin_segment_center(self, text, layer, start, end):
|
||||
"""
|
||||
Creates a path like pin with center-line convention
|
||||
"""
|
||||
|
||||
debug.check(start.x == end.x or start.y == end.y,
|
||||
"Cannot have a non-manhatten layout pin.")
|
||||
|
||||
if start.x != end.x and start.y != end.y:
|
||||
file_name = "non_rectilinear.gds"
|
||||
self.gds_write(file_name)
|
||||
debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1)
|
||||
|
||||
minwidth_layer = drc["minwidth_{}".format(layer)]
|
||||
|
||||
# one of these will be zero
|
||||
|
|
@ -516,12 +517,12 @@ class layout():
|
|||
self.connect_inst([])
|
||||
return inst
|
||||
|
||||
def add_via_stack(self, offset, direction, from_layer, to_layer,
|
||||
size=[1,1]):
|
||||
def add_via_stack(self, offset, from_layer, to_layer,
|
||||
direction=None,
|
||||
size=[1, 1]):
|
||||
"""
|
||||
Punch a stack of vias from a start layer to a target layer.
|
||||
"""
|
||||
|
||||
return self.__add_via_stack_internal(offset=offset,
|
||||
direction=direction,
|
||||
from_layer=from_layer,
|
||||
|
|
@ -530,8 +531,9 @@ class layout():
|
|||
last_via=None,
|
||||
size=size)
|
||||
|
||||
def add_via_stack_center(self, offset, direction, from_layer, to_layer,
|
||||
size=[1,1]):
|
||||
def add_via_stack_center(self, offset, from_layer, to_layer,
|
||||
direction=None,
|
||||
size=[1, 1]):
|
||||
"""
|
||||
Punch a stack of vias from a start layer to a target layer by the center
|
||||
coordinate accounting for mirroring and rotation.
|
||||
|
|
@ -544,7 +546,6 @@ class layout():
|
|||
last_via=None,
|
||||
size=size)
|
||||
|
||||
|
||||
def __add_via_stack_internal(self, offset, direction, from_layer, to_layer,
|
||||
via_func, last_via, size):
|
||||
"""
|
||||
|
|
@ -580,7 +581,6 @@ class layout():
|
|||
last_via=via,
|
||||
size=size)
|
||||
|
||||
|
||||
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
|
||||
"""Adds a ptx module to the design."""
|
||||
import ptx
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ from globals import OPTS
|
|||
class bank(design.design):
|
||||
"""
|
||||
Dynamically generated a single bank including bitcell array,
|
||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
||||
write driver and sense amplifiers.
|
||||
This can create up to two ports in any combination: rw, w, r.
|
||||
This can create up to two ports in any combination: rw, w, r.
|
||||
"""
|
||||
|
||||
def __init__(self, sram_config, name=""):
|
||||
|
|
@ -27,15 +27,15 @@ class bank(design.design):
|
|||
self.sram_config = sram_config
|
||||
sram_config.set_local_config(self)
|
||||
if self.write_size:
|
||||
self.num_wmasks = int(self.word_size/self.write_size)
|
||||
self.num_wmasks = int(self.word_size / self.write_size)
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
if name == "":
|
||||
name = "bank_{0}_{1}".format(self.word_size, self.num_words)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words))
|
||||
|
||||
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
|
||||
self.num_words))
|
||||
|
||||
# The local control signals are gated when we have bank select logic,
|
||||
# so this prefix will be added to all of the input signals to create
|
||||
|
|
@ -47,18 +47,17 @@ class bank(design.design):
|
|||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
|
||||
debug.check(len(self.all_ports)<=2,
|
||||
"Bank layout cannot handle more than two ports.")
|
||||
self.create_layout()
|
||||
self.add_boundary()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
|
||||
self.compute_sizes()
|
||||
self.add_modules()
|
||||
self.add_pins() # Must create the replica bitcell array first
|
||||
self.create_instances()
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
self.place_instances()
|
||||
|
|
@ -66,35 +65,34 @@ class bank(design.design):
|
|||
self.route_layout()
|
||||
|
||||
# Can remove the following, but it helps for debug!
|
||||
#self.add_lvs_correspondence_points()
|
||||
# self.add_lvs_correspondence_points()
|
||||
|
||||
# Remember the bank center for further placement
|
||||
self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1)
|
||||
self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1)
|
||||
self.bank_array_ur = self.bitcell_array_inst.ur()
|
||||
self.bank_array_ul = self.bitcell_array_inst.ul()
|
||||
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
""" Adding pins for Bank module"""
|
||||
for port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT")
|
||||
self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT")
|
||||
for port in self.all_ports:
|
||||
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT")
|
||||
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("din{0}_{1}".format(port,bit),"INPUT")
|
||||
self.add_pin("din{0}_{1}".format(port,bit), "INPUT")
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.addr_size):
|
||||
self.add_pin("addr{0}_{1}".format(port,bit),"INPUT")
|
||||
self.add_pin("addr{0}_{1}".format(port,bit), "INPUT")
|
||||
|
||||
# For more than one bank, we have a bank select and name
|
||||
# the signals gated_*.
|
||||
if self.num_banks > 1:
|
||||
for port in self.all_ports:
|
||||
self.add_pin("bank_sel{}".format(port),"INPUT")
|
||||
self.add_pin("bank_sel{}".format(port), "INPUT")
|
||||
for port in self.read_ports:
|
||||
self.add_pin("s_en{0}".format(port), "INPUT")
|
||||
for port in self.all_ports:
|
||||
|
|
@ -102,12 +100,11 @@ class bank(design.design):
|
|||
for port in self.write_ports:
|
||||
self.add_pin("w_en{0}".format(port), "INPUT")
|
||||
for bit in range(self.num_wmasks):
|
||||
self.add_pin("bank_wmask{0}_{1}".format(port,bit),"INPUT")
|
||||
self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT")
|
||||
for port in self.all_ports:
|
||||
self.add_pin("wl_en{0}".format(port), "INPUT")
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def route_layout(self):
|
||||
""" Create routing amoung the modules """
|
||||
|
|
@ -124,18 +121,27 @@ class bank(design.design):
|
|||
|
||||
self.route_supplies()
|
||||
|
||||
def route_rbl(self,port):
|
||||
def route_rbl(self, port):
|
||||
""" Route the rbl_bl and rbl_wl """
|
||||
|
||||
bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
||||
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
|
||||
self.add_layout_pin(text="rbl_bl{0}".format(port),
|
||||
layer=bl_pin.layer,
|
||||
offset=bl_pin.ll(),
|
||||
height=bl_pin.height(),
|
||||
width=bl_pin.width())
|
||||
|
||||
|
||||
# This will ensure the pin is only on the top or bottom edge
|
||||
if port % 2:
|
||||
via_offset = bl_pin.uc() + vector(0, self.m2_pitch)
|
||||
left_right_offset = vector(self.max_x_offset, via_offset.y)
|
||||
else:
|
||||
via_offset = bl_pin.bc() - vector(0, self.m2_pitch)
|
||||
left_right_offset = vector(self.min_x_offset, via_offset.y)
|
||||
if bl_pin == "m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=via_offset)
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=via_offset)
|
||||
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
|
||||
layer="m3",
|
||||
start=left_right_offset,
|
||||
end=via_offset)
|
||||
|
||||
def route_bitlines(self, port):
|
||||
""" Route the bitlines depending on the port type rw, w, or r. """
|
||||
|
|
@ -146,7 +152,6 @@ class bank(design.design):
|
|||
self.route_port_data_out(port)
|
||||
self.route_port_data_to_bitcell_array(port)
|
||||
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the instances of the netlist. """
|
||||
|
||||
|
|
@ -161,19 +166,18 @@ class bank(design.design):
|
|||
Compute the empty instance offsets for port0 and port1 (if needed)
|
||||
"""
|
||||
|
||||
self.port_data_offsets = [None]*len(self.all_ports)
|
||||
self.port_address_offsets = [None]*len(self.all_ports)
|
||||
|
||||
self.column_decoder_offsets = [None]*len(self.all_ports)
|
||||
self.bank_select_offsets = [None]*len(self.all_ports)
|
||||
self.port_data_offsets = [None] * len(self.all_ports)
|
||||
self.port_address_offsets = [None] * len(self.all_ports)
|
||||
|
||||
self.column_decoder_offsets = [None] * len(self.all_ports)
|
||||
self.bank_select_offsets = [None] * len(self.all_ports)
|
||||
|
||||
# The center point for these cells are the upper-right corner of
|
||||
# the bitcell array.
|
||||
# The port address decoder/driver logic is placed on the right and mirrored on Y-axis.
|
||||
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
|
||||
self.bitcell_array_top = self.bitcell_array.height
|
||||
self.bitcell_array_right = self.bitcell_array.width
|
||||
self.bitcell_array_top = self.bitcell_array.height
|
||||
self.bitcell_array_right = self.bitcell_array.width
|
||||
|
||||
# These are the offsets of the main array (excluding dummy and replica rows/cols)
|
||||
self.main_bitcell_array_top = self.bitcell_array.bitcell_array_inst.uy()
|
||||
|
|
@ -185,7 +189,6 @@ class bank(design.design):
|
|||
self.compute_instance_port0_offsets()
|
||||
if len(self.all_ports)==2:
|
||||
self.compute_instance_port1_offsets()
|
||||
|
||||
|
||||
def compute_instance_port0_offsets(self):
|
||||
"""
|
||||
|
|
@ -196,29 +199,30 @@ class bank(design.design):
|
|||
|
||||
# UPPER RIGHT QUADRANT
|
||||
# Bitcell array is placed at (0,0)
|
||||
self.bitcell_array_offset = vector(0,0)
|
||||
self.bitcell_array_offset = vector(0, 0)
|
||||
|
||||
# LOWER RIGHT QUADRANT
|
||||
# Below the bitcell array
|
||||
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0)
|
||||
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0)
|
||||
|
||||
# UPPER LEFT QUADRANT
|
||||
# To the left of the bitcell array
|
||||
x_offset = self.m2_gap + self.port_address.width
|
||||
self.port_address_offsets[port] = vector(-x_offset,self.main_bitcell_array_bottom)
|
||||
x_offset = self.m2_gap + self.port_address.width
|
||||
self.port_address_offsets[port] = vector(-x_offset,
|
||||
self.main_bitcell_array_bottom)
|
||||
|
||||
# LOWER LEFT QUADRANT
|
||||
# Place the col decoder left aligned with wordline driver
|
||||
# Place the col decoder left aligned with wordline driver
|
||||
# This is also placed so that it's supply rails do not align with the SRAM-level
|
||||
# control logic to allow control signals to easily pass over in M3
|
||||
# by placing 1/2 a cell pitch down
|
||||
x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width
|
||||
if self.col_addr_size > 0:
|
||||
x_offset += self.column_decoder.width + self.col_addr_bus_width
|
||||
y_offset = 0.5*self.dff.height + self.column_decoder.height
|
||||
y_offset = 0.5 * self.dff.height + self.column_decoder.height
|
||||
else:
|
||||
y_offset = 0
|
||||
self.column_decoder_offsets[port] = vector(-x_offset,-y_offset)
|
||||
self.column_decoder_offsets[port] = vector(-x_offset, -y_offset)
|
||||
|
||||
# Bank select gets placed below the column decoder (x_offset doesn't change)
|
||||
if self.col_addr_size > 0:
|
||||
|
|
@ -227,7 +231,7 @@ class bank(design.design):
|
|||
y_offset = self.port_address_offsets[port].y
|
||||
if self.num_banks > 1:
|
||||
y_offset += self.bank_select.height + drc("well_to_well")
|
||||
self.bank_select_offsets[port] = vector(-x_offset,-y_offset)
|
||||
self.bank_select_offsets[port] = vector(-x_offset, -y_offset)
|
||||
|
||||
def compute_instance_port1_offsets(self):
|
||||
"""
|
||||
|
|
@ -246,18 +250,19 @@ class bank(design.design):
|
|||
# LOWER RIGHT QUADRANT
|
||||
# To the left of the bitcell array
|
||||
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap
|
||||
self.port_address_offsets[port] = vector(x_offset,self.main_bitcell_array_bottom)
|
||||
self.port_address_offsets[port] = vector(x_offset,
|
||||
self.main_bitcell_array_bottom)
|
||||
|
||||
# UPPER RIGHT QUADRANT
|
||||
# Place the col decoder right aligned with wordline driver
|
||||
# Place the col decoder right aligned with wordline driver
|
||||
# Above the bitcell array with a well spacing
|
||||
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width
|
||||
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width
|
||||
if self.col_addr_size > 0:
|
||||
x_offset += self.column_decoder.width + self.col_addr_bus_width
|
||||
y_offset = self.bitcell_array_top + 0.5*self.dff.height + self.column_decoder.height
|
||||
y_offset = self.bitcell_array_top + 0.5 * self.dff.height + self.column_decoder.height
|
||||
else:
|
||||
y_offset = self.bitcell_array_top
|
||||
self.column_decoder_offsets[port] = vector(x_offset,y_offset)
|
||||
self.column_decoder_offsets[port] = vector(x_offset, y_offset)
|
||||
|
||||
# Bank select gets placed above the column decoder (x_offset doesn't change)
|
||||
if self.col_addr_size > 0:
|
||||
|
|
@ -265,7 +270,7 @@ class bank(design.design):
|
|||
self.port_data[port].column_mux_offset.y + self.port_data[port].column_mux_array.height)
|
||||
else:
|
||||
y_offset = self.port_address_offsets[port].y
|
||||
self.bank_select_offsets[port] = vector(x_offset,y_offset)
|
||||
self.bank_select_offsets[port] = vector(x_offset, y_offset)
|
||||
|
||||
def place_instances(self):
|
||||
""" Place the instances. """
|
||||
|
|
@ -281,19 +286,20 @@ class bank(design.design):
|
|||
self.place_column_decoder(self.column_decoder_offsets)
|
||||
self.place_bank_select(self.bank_select_offsets)
|
||||
|
||||
|
||||
def compute_sizes(self):
|
||||
""" Computes the required sizes to create the bank """
|
||||
|
||||
self.num_cols = int(self.words_per_row*self.word_size)
|
||||
self.num_cols = int(self.words_per_row * self.word_size)
|
||||
self.num_rows = int(self.num_words / self.words_per_row)
|
||||
|
||||
self.row_addr_size = int(log(self.num_rows, 2))
|
||||
self.col_addr_size = int(log(self.words_per_row, 2))
|
||||
self.addr_size = self.col_addr_size + self.row_addr_size
|
||||
|
||||
debug.check(self.num_rows*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.")
|
||||
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.")
|
||||
debug.check(self.num_rows * self.num_cols==self.word_size * self.num_words,
|
||||
"Invalid bank sizes.")
|
||||
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,
|
||||
"Invalid address break down.")
|
||||
|
||||
# The order of the control signals on the control bus:
|
||||
self.input_control_signals = []
|
||||
|
|
@ -312,13 +318,13 @@ class bank(design.design):
|
|||
self.num_control_lines = [len(x) for x in self.input_control_signals]
|
||||
|
||||
# The width of this bus is needed to place other modules (e.g. decoder) for each port
|
||||
self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines]
|
||||
self.central_bus_width = [self.m2_pitch * x + self.m2_width for x in self.num_control_lines]
|
||||
|
||||
# These will be outputs of the gaters if this is multibank, if not, normal signals.
|
||||
self.control_signals = []
|
||||
for port in self.all_ports:
|
||||
if self.num_banks > 1:
|
||||
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
|
||||
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
|
||||
else:
|
||||
self.control_signals.append(self.input_control_signals[port])
|
||||
|
||||
|
|
@ -326,19 +332,18 @@ class bank(design.design):
|
|||
if self.col_addr_size>0:
|
||||
self.num_col_addr_lines = 2**self.col_addr_size
|
||||
else:
|
||||
self.num_col_addr_lines = 0
|
||||
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
|
||||
self.num_col_addr_lines = 0
|
||||
self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
|
||||
|
||||
# A space for wells or jogging m2
|
||||
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3*self.m2_pitch)
|
||||
|
||||
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3 * self.m2_pitch)
|
||||
|
||||
def add_modules(self):
|
||||
""" Add all the modules using the class loader """
|
||||
|
||||
# create arrays of bitline and bitline_bar names for read, write, or all ports
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bl_names = self.bitcell.get_all_bl_names()
|
||||
self.br_names = self.bitcell.get_all_br_names()
|
||||
self.wl_names = self.bitcell.get_all_wl_names()
|
||||
|
|
@ -352,13 +357,11 @@ class bank(design.design):
|
|||
self.port_data.append(temp_pre)
|
||||
self.add_mod(self.port_data[port])
|
||||
|
||||
|
||||
self.port_address = factory.create(module_type="port_address",
|
||||
cols=self.num_cols,
|
||||
rows=self.num_rows)
|
||||
self.add_mod(self.port_address)
|
||||
|
||||
|
||||
self.port_rbl_map = self.all_ports
|
||||
self.num_rbl = len(self.all_ports)
|
||||
|
||||
|
|
@ -370,22 +373,20 @@ class bank(design.design):
|
|||
bitcell_ports=self.all_ports)
|
||||
self.add_mod(self.bitcell_array)
|
||||
|
||||
|
||||
if(self.num_banks > 1):
|
||||
self.bank_select = factory.create(module_type="bank_select")
|
||||
self.add_mod(self.bank_select)
|
||||
|
||||
|
||||
def create_bitcell_array(self):
|
||||
""" Creating Bitcell Array """
|
||||
|
||||
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
|
||||
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
|
||||
mod=self.bitcell_array)
|
||||
|
||||
temp = []
|
||||
for col in range(self.num_cols):
|
||||
for bitline in self.bitline_names:
|
||||
temp.append("{0}_{1}".format(bitline,col))
|
||||
temp.append("{0}_{1}".format(bitline, col))
|
||||
for rbl in range(self.num_rbl):
|
||||
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl)
|
||||
temp.append(rbl_bl_name)
|
||||
|
|
@ -393,23 +394,21 @@ class bank(design.design):
|
|||
temp.append(rbl_br_name)
|
||||
for row in range(self.num_rows):
|
||||
for wordline in self.wl_names:
|
||||
temp.append("{0}_{1}".format(wordline,row))
|
||||
temp.append("{0}_{1}".format(wordline, row))
|
||||
for port in self.all_ports:
|
||||
temp.append("wl_en{0}".format(port))
|
||||
temp.append("vdd")
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_bitcell_array(self, offset):
|
||||
""" Placing Bitcell Array """
|
||||
self.bitcell_array_inst.place(offset)
|
||||
|
||||
|
||||
def create_port_data(self):
|
||||
""" Creating Port Data """
|
||||
|
||||
self.port_data_inst = [None]*len(self.all_ports)
|
||||
self.port_data_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port),
|
||||
mod=self.port_data[port])
|
||||
|
|
@ -419,17 +418,17 @@ class bank(design.design):
|
|||
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
|
||||
temp.append(rbl_bl_name)
|
||||
temp.append(rbl_br_name)
|
||||
for col in range(self.num_cols):
|
||||
temp.append("{0}_{1}".format(self.bl_names[port],col))
|
||||
temp.append("{0}_{1}".format(self.br_names[port],col))
|
||||
for col in range(self.num_cols):
|
||||
temp.append("{0}_{1}".format(self.bl_names[port], col))
|
||||
temp.append("{0}_{1}".format(self.br_names[port], col))
|
||||
if port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
temp.append("dout{0}_{1}".format(port,bit))
|
||||
temp.append("dout{0}_{1}".format(port, bit))
|
||||
if port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
temp.append("din{0}_{1}".format(port,bit))
|
||||
temp.append("din{0}_{1}".format(port, bit))
|
||||
# Will be empty if no col addr lines
|
||||
sel_names = ["sel{0}_{1}".format(port,x) for x in range(self.num_col_addr_lines)]
|
||||
sel_names = ["sel{0}_{1}".format(port, x) for x in range(self.num_col_addr_lines)]
|
||||
temp.extend(sel_names)
|
||||
if port in self.read_ports:
|
||||
temp.append("s_en{0}".format(port))
|
||||
|
|
@ -438,17 +437,16 @@ class bank(design.design):
|
|||
temp.append("w_en{0}".format(port))
|
||||
for bit in range(self.num_wmasks):
|
||||
temp.append("bank_wmask{0}_{1}".format(port, bit))
|
||||
temp.extend(["vdd","gnd"])
|
||||
temp.extend(["vdd", "gnd"])
|
||||
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_port_data(self, offsets):
|
||||
""" Placing Port Data """
|
||||
|
||||
for port in self.all_ports:
|
||||
# Top one is unflipped, bottom is flipped along X direction
|
||||
if port%2 == 1:
|
||||
if port % 2 == 1:
|
||||
mirror = "R0"
|
||||
else:
|
||||
mirror = "MX"
|
||||
|
|
@ -457,42 +455,40 @@ class bank(design.design):
|
|||
def create_port_address(self):
|
||||
""" Create the hierarchical row decoder """
|
||||
|
||||
self.port_address_inst = [None]*len(self.all_ports)
|
||||
self.port_address_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
|
||||
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
|
||||
mod=self.port_address)
|
||||
|
||||
temp = []
|
||||
for bit in range(self.row_addr_size):
|
||||
temp.append("addr{0}_{1}".format(port,bit+self.col_addr_size))
|
||||
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
|
||||
temp.append("wl_en{0}".format(port))
|
||||
for row in range(self.num_rows):
|
||||
temp.append("{0}_{1}".format(self.wl_names[port],row))
|
||||
temp.append("{0}_{1}".format(self.wl_names[port], row))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_port_address(self, offsets):
|
||||
""" Place the hierarchical row decoder """
|
||||
|
||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
|
||||
|
||||
# The address and control bus will be in between decoder and the main memory array
|
||||
# This bus will route address bits to the decoder input and column mux inputs.
|
||||
# The address and control bus will be in between decoder and the main memory array
|
||||
# This bus will route address bits to the decoder input and column mux inputs.
|
||||
# The wires are actually routed after we placed the stuff on both sides.
|
||||
# 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.
|
||||
|
||||
for port in self.all_ports:
|
||||
if port%2:
|
||||
if port % 2:
|
||||
mirror = "MY"
|
||||
else:
|
||||
mirror = "R0"
|
||||
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||
|
||||
|
||||
def create_column_decoder(self):
|
||||
"""
|
||||
"""
|
||||
Create a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
|
||||
|
|
@ -510,40 +506,38 @@ class bank(design.design):
|
|||
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height)
|
||||
else:
|
||||
# No error checking before?
|
||||
debug.error("Invalid column decoder?",-1)
|
||||
debug.error("Invalid column decoder?", -1)
|
||||
self.add_mod(self.column_decoder)
|
||||
|
||||
self.column_decoder_inst = [None]*len(self.all_ports)
|
||||
self.column_decoder_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
|
||||
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
|
||||
mod=self.column_decoder)
|
||||
|
||||
temp = []
|
||||
for bit in range(self.col_addr_size):
|
||||
temp.append("addr{0}_{1}".format(port,bit))
|
||||
temp.append("addr{0}_{1}".format(port, bit))
|
||||
for bit in range(self.num_col_addr_lines):
|
||||
temp.append("sel{0}_{1}".format(port,bit))
|
||||
temp.append("sel{0}_{1}".format(port, bit))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_column_decoder(self, offsets):
|
||||
"""
|
||||
"""
|
||||
Place a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
if self.col_addr_size == 0:
|
||||
return
|
||||
|
||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.")
|
||||
debug.check(len(offsets)>=len(self.all_ports),
|
||||
"Insufficient offsets to place column decoder.")
|
||||
|
||||
for port in self.all_ports:
|
||||
if port%2 == 1:
|
||||
if port % 2 == 1:
|
||||
mirror = "XY"
|
||||
else:
|
||||
mirror = "R0"
|
||||
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||
|
||||
|
||||
|
||||
def create_bank_select(self):
|
||||
""" Create the bank select logic. """
|
||||
|
|
@ -551,7 +545,7 @@ class bank(design.design):
|
|||
if not self.num_banks > 1:
|
||||
return
|
||||
|
||||
self.bank_select_inst = [None]*len(self.all_ports)
|
||||
self.bank_select_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
|
||||
mod=self.bank_select)
|
||||
|
|
@ -563,24 +557,23 @@ class bank(design.design):
|
|||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_bank_select(self, offsets):
|
||||
""" Place the bank select logic. """
|
||||
|
||||
if not self.num_banks > 1:
|
||||
return
|
||||
|
||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place bank select logic.")
|
||||
debug.check(len(offsets)>=len(self.all_ports),
|
||||
"Insufficient offsets to place bank select logic.")
|
||||
|
||||
for port in self.all_ports:
|
||||
self.bank_select_inst[port].place(offsets[port])
|
||||
|
||||
|
||||
def route_supplies(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
for inst in self.insts:
|
||||
self.copy_power_pins(inst,"vdd")
|
||||
self.copy_power_pins(inst,"gnd")
|
||||
self.copy_power_pins(inst, "vdd")
|
||||
self.copy_power_pins(inst, "gnd")
|
||||
|
||||
def route_bank_select(self, port):
|
||||
""" Route the bank select logic. """
|
||||
|
|
@ -595,7 +588,7 @@ class bank(design.design):
|
|||
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
|
||||
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
|
||||
|
||||
copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)]
|
||||
copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)]
|
||||
for signal in range(len(copy_control_signals)):
|
||||
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
|
||||
|
||||
|
|
@ -604,25 +597,24 @@ class bank(design.design):
|
|||
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
|
||||
name = self.control_signals[port][signal]
|
||||
bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y)
|
||||
self.add_path("m3",[out_pos, bus_pos])
|
||||
self.add_path("m3", [out_pos, bus_pos])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=bus_pos)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=out_pos)
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=out_pos)
|
||||
|
||||
|
||||
def setup_routing_constraints(self):
|
||||
"""
|
||||
"""
|
||||
After the modules are instantiated, find the dimensions for the
|
||||
control bus, power ring, etc.
|
||||
control bus, power ring, etc.
|
||||
"""
|
||||
|
||||
self.max_y_offset = max([x.uy() for x in self.insts]) + 3*self.m1_width
|
||||
self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width
|
||||
self.min_y_offset = min([x.by() for x in self.insts])
|
||||
|
||||
self.max_x_offset = max([x.rx() for x in self.insts]) + 3*self.m1_width
|
||||
self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width
|
||||
self.min_x_offset = min([x.lx() for x in self.insts])
|
||||
|
||||
# # Create the core bbox for the power rings
|
||||
|
|
@ -632,7 +624,6 @@ class bank(design.design):
|
|||
|
||||
self.height = ur.y - ll.y
|
||||
self.width = ur.x - ll.x
|
||||
|
||||
|
||||
def route_central_bus(self):
|
||||
""" Create the address, supply, and control signal central bus lines. """
|
||||
|
|
@ -640,13 +631,13 @@ class bank(design.design):
|
|||
# Overall central bus width. It includes all the column mux lines,
|
||||
# and control lines.
|
||||
|
||||
self.bus_xoffset = [None]*len(self.all_ports)
|
||||
self.bus_xoffset = [None] * len(self.all_ports)
|
||||
# Port 0
|
||||
# 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
|
||||
# 2 pitches on the right for vias/jogs to access the inputs
|
||||
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset)
|
||||
# The control bus is routed up to two pitches below the bitcell array
|
||||
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2*self.m1_pitch
|
||||
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
|
||||
self.bus_xoffset[0] = self.create_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=control_bus_offset,
|
||||
|
|
@ -658,7 +649,7 @@ class bank(design.design):
|
|||
# Port 1
|
||||
if len(self.all_ports)==2:
|
||||
# The other control bus is routed up to two pitches above the bitcell array
|
||||
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2*self.m1_pitch
|
||||
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
|
||||
control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch,
|
||||
self.max_y_offset - control_bus_length)
|
||||
# The bus for the right port is reversed so that the rbl_wl is closest to the array
|
||||
|
|
@ -669,7 +660,6 @@ class bank(design.design):
|
|||
length=control_bus_length,
|
||||
vertical=True,
|
||||
make_pins=(self.num_banks==1))
|
||||
|
||||
|
||||
def route_port_data_to_bitcell_array(self, port):
|
||||
""" Routing of BL and BR between port data and bitcell array """
|
||||
|
|
@ -677,15 +667,19 @@ class bank(design.design):
|
|||
# Connect the regular bitlines
|
||||
inst2 = self.port_data_inst[port]
|
||||
inst1 = self.bitcell_array_inst
|
||||
inst1_bl_name = self.bl_names[port]+"_{}"
|
||||
inst1_br_name = self.br_names[port]+"_{}"
|
||||
inst1_bl_name = self.bl_names[port] + "_{}"
|
||||
inst1_br_name = self.br_names[port] + "_{}"
|
||||
|
||||
inst2_bl_name = inst2.mod.get_bl_names()+"_{}"
|
||||
inst2_br_name = inst2.mod.get_br_names()+"_{}"
|
||||
inst2_bl_name = inst2.mod.get_bl_names() + "_{}"
|
||||
inst2_br_name = inst2.mod.get_br_names() + "_{}"
|
||||
|
||||
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
|
||||
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name,
|
||||
inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name)
|
||||
self.connect_bitlines(inst1=inst1,
|
||||
inst2=inst2,
|
||||
num_bits=self.num_cols,
|
||||
inst1_bl_name=inst1_bl_name,
|
||||
inst1_br_name=inst1_br_name,
|
||||
inst2_bl_name=inst2_bl_name,
|
||||
inst2_br_name=inst2_br_name)
|
||||
|
||||
# Connect the replica bitlines
|
||||
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
||||
|
|
@ -693,20 +687,17 @@ class bank(design.design):
|
|||
self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl")
|
||||
self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br")
|
||||
|
||||
|
||||
|
||||
def route_port_data_out(self, port):
|
||||
""" Add pins for the port data out """
|
||||
|
||||
for bit in range(self.word_size):
|
||||
data_pin = self.port_data_inst[port].get_pin("dout_{0}".format(bit))
|
||||
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port,bit),
|
||||
layer=data_pin.layer,
|
||||
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port, bit),
|
||||
layer=data_pin.layer,
|
||||
offset=data_pin.center(),
|
||||
height=data_pin.height(),
|
||||
width=data_pin.width())
|
||||
|
||||
|
||||
def route_port_address_in(self, port):
|
||||
""" Routes the row decoder inputs and supplies """
|
||||
|
||||
|
|
@ -714,17 +705,15 @@ class bank(design.design):
|
|||
for row in range(self.row_addr_size):
|
||||
addr_idx = row + self.col_addr_size
|
||||
decoder_name = "addr_{}".format(row)
|
||||
addr_name = "addr{0}_{1}".format(port,addr_idx)
|
||||
addr_name = "addr{0}_{1}".format(port, addr_idx)
|
||||
self.copy_layout_pin(self.port_address_inst[port], decoder_name, addr_name)
|
||||
|
||||
|
||||
|
||||
def route_port_data_in(self, port):
|
||||
""" Connecting port data in """
|
||||
|
||||
for row in range(self.word_size):
|
||||
data_name = "din_{}".format(row)
|
||||
din_name = "din{0}_{1}".format(port,row)
|
||||
din_name = "din{0}_{1}".format(port, row)
|
||||
self.copy_layout_pin(self.port_data_inst[port], data_name, din_name)
|
||||
|
||||
if self.word_size:
|
||||
|
|
@ -732,8 +721,6 @@ class bank(design.design):
|
|||
wmask_name = "bank_wmask_{}".format(row)
|
||||
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
|
||||
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
|
||||
|
||||
|
||||
|
||||
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
|
||||
|
|
@ -751,19 +738,18 @@ class bank(design.design):
|
|||
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name)
|
||||
(top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name)
|
||||
|
||||
|
||||
# Channel route each mux separately since we don't minimize the number
|
||||
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
||||
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
|
||||
offset = bottom_inst.ul() + vector(0, self.m1_pitch)
|
||||
for bit in range(num_bits):
|
||||
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
|
||||
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
|
||||
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
|
||||
route_map = list(zip(bottom_names, top_names))
|
||||
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
|
||||
|
||||
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
|
||||
"""
|
||||
Connect two pins of two modules.
|
||||
Connect two pins of two modules.
|
||||
This assumes that they have sufficient space to create a jog
|
||||
in the middle between the two modules (if needed).
|
||||
"""
|
||||
|
|
@ -784,11 +770,13 @@ class bank(design.design):
|
|||
bottom_loc = bottom_pin.uc()
|
||||
top_loc = top_pin.bc()
|
||||
|
||||
yoffset = 0.5*(top_loc.y+bottom_loc.y)
|
||||
self.add_path(top_pin.layer,[bottom_loc, vector(bottom_loc.x,yoffset),
|
||||
vector(top_loc.x,yoffset), top_loc])
|
||||
yoffset = 0.5 * (top_loc.y + bottom_loc.y)
|
||||
self.add_path(top_pin.layer,
|
||||
[bottom_loc,
|
||||
vector(bottom_loc.x, yoffset),
|
||||
vector(top_loc.x, yoffset),
|
||||
top_loc])
|
||||
|
||||
|
||||
def connect_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bl_name, inst1_br_name,
|
||||
inst2_bl_name, inst2_br_name):
|
||||
|
|
@ -799,14 +787,13 @@ class bank(design.design):
|
|||
for col in range(num_bits):
|
||||
self.connect_bitline(inst1, inst2, inst1_bl_name.format(col), inst2_bl_name.format(col))
|
||||
self.connect_bitline(inst1, inst2, inst1_br_name.format(col), inst2_br_name.format(col))
|
||||
|
||||
|
||||
def route_port_address(self, port):
|
||||
""" Connect Wordline driver to bitcell array wordline """
|
||||
|
||||
self.route_port_address_in(port)
|
||||
|
||||
if port%2:
|
||||
if port % 2:
|
||||
self.route_port_address_right(port)
|
||||
else:
|
||||
self.route_port_address_left(port)
|
||||
|
|
@ -817,21 +804,20 @@ class bank(design.design):
|
|||
for row in range(self.num_rows):
|
||||
# The mid guarantees we exit the input cell to the right.
|
||||
driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).rc()
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc()
|
||||
mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].rx() + 0.5*self.bitcell_array_inst.lx(),0)
|
||||
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0.5,1)
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).lc()
|
||||
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0)
|
||||
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1)
|
||||
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||
|
||||
|
||||
def route_port_address_right(self, port):
|
||||
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||
|
||||
for row in range(self.num_rows):
|
||||
# The mid guarantees we exit the input cell to the right.
|
||||
driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).lc()
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).rc()
|
||||
mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].lx() + 0.5*self.bitcell_array_inst.rx(),0)
|
||||
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0,1)
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).rc()
|
||||
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0)
|
||||
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
|
||||
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||
|
||||
def route_column_address_lines(self, port):
|
||||
|
|
@ -845,7 +831,7 @@ class bank(design.design):
|
|||
decode_names = ["Zb", "Z"]
|
||||
|
||||
# The Address LSB
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
||||
|
||||
elif self.col_addr_size > 1:
|
||||
decode_names = []
|
||||
|
|
@ -854,11 +840,11 @@ class bank(design.design):
|
|||
|
||||
for i in range(self.col_addr_size):
|
||||
decoder_name = "in_{}".format(i)
|
||||
addr_name = "addr{0}_{1}".format(port,i)
|
||||
addr_name = "addr{0}_{1}".format(port, i)
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||
|
||||
if port%2:
|
||||
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
|
||||
if port % 2:
|
||||
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_pitch, 0)
|
||||
else:
|
||||
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
||||
|
||||
|
|
@ -871,7 +857,8 @@ class bank(design.design):
|
|||
self.create_vertical_channel_route(route_map, offset, self.m1_stack)
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
""" This adds some points for easier debugging if LVS goes wrong.
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
|
|
@ -880,7 +867,7 @@ class bank(design.design):
|
|||
wl_name = "wl_{}".format(i)
|
||||
wl_pin = self.bitcell_array_inst.get_pin(wl_name)
|
||||
self.add_label(text=wl_name,
|
||||
layer="m1",
|
||||
layer="m1",
|
||||
offset=wl_pin.center())
|
||||
|
||||
# Add the bitline names
|
||||
|
|
@ -890,35 +877,35 @@ class bank(design.design):
|
|||
bl_pin = self.bitcell_array_inst.get_pin(bl_name)
|
||||
br_pin = self.bitcell_array_inst.get_pin(br_name)
|
||||
self.add_label(text=bl_name,
|
||||
layer="m2",
|
||||
layer="m2",
|
||||
offset=bl_pin.center())
|
||||
self.add_label(text=br_name,
|
||||
layer="m2",
|
||||
layer="m2",
|
||||
offset=br_pin.center())
|
||||
|
||||
# # Add the data output names to the sense amp output
|
||||
# # Add the data output names to the sense amp output
|
||||
# for i in range(self.word_size):
|
||||
# data_name = "data_{}".format(i)
|
||||
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
|
||||
# self.add_label(text="sa_out_{}".format(i),
|
||||
# layer="m2",
|
||||
# layer="m2",
|
||||
# offset=data_pin.center())
|
||||
|
||||
# Add labels on the decoder
|
||||
for port in self.write_ports:
|
||||
for i in range(self.word_size):
|
||||
data_name = "dec_out_{}".format(i)
|
||||
pin_name = "in_{}".format(i)
|
||||
pin_name = "in_{}".format(i)
|
||||
data_pin = self.wordline_driver_inst[port].get_pin(pin_name)
|
||||
self.add_label(text=data_name,
|
||||
layer="m1",
|
||||
layer="m1",
|
||||
offset=data_pin.center())
|
||||
|
||||
def route_control_lines(self, port):
|
||||
""" Route the control lines of the entire bank """
|
||||
|
||||
# Make a list of tuples that we will connect.
|
||||
# From control signal to the module pin
|
||||
# From control signal to the module pin
|
||||
# Connection from the central bus to the main control block crosses
|
||||
# pre-decoder and this connection is in metal3
|
||||
connection = []
|
||||
|
|
@ -943,58 +930,59 @@ class bank(design.design):
|
|||
|
||||
for (control_signal, pin_pos) in connection:
|
||||
control_mid_pos = self.bus_xoffset[port][control_signal]
|
||||
control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y)
|
||||
control_pos = vector(self.bus_xoffset[port][control_signal].x, pin_pos.y)
|
||||
self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=control_pos)
|
||||
|
||||
|
||||
# clk to wordline_driver
|
||||
control_signal = self.prefix+"wl_en{}".format(port)
|
||||
if port%2:
|
||||
control_signal = self.prefix + "wl_en{}".format(port)
|
||||
if port % 2:
|
||||
pin_pos = self.port_address_inst[port].get_pin("wl_en").uc()
|
||||
mid_pos = pin_pos + vector(0,2*self.m2_gap) # to route down to the top of the bus
|
||||
mid_pos = pin_pos + vector(0, 2 * self.m2_gap) # to route down to the top of the bus
|
||||
else:
|
||||
pin_pos = self.port_address_inst[port].get_pin("wl_en").bc()
|
||||
mid_pos = pin_pos - vector(0,2*self.m2_gap) # to route down to the top of the bus
|
||||
mid_pos = pin_pos - vector(0, 2 * self.m2_gap) # to route down to the top of the bus
|
||||
control_x_offset = self.bus_xoffset[port][control_signal].x
|
||||
control_pos = vector(control_x_offset, mid_pos.y)
|
||||
self.add_wire(self.m1_stack,[pin_pos, mid_pos, control_pos])
|
||||
self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=control_pos)
|
||||
|
||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
|
||||
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption
|
||||
# Decoder is assumed to have settled before the negative edge of the clock.
|
||||
# Delay model relies on this assumption
|
||||
stage_effort_list = []
|
||||
wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout
|
||||
stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,inp_is_rise)
|
||||
stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,
|
||||
inp_is_rise)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_wl_en_cin(self):
|
||||
"""Get the relative capacitance of all the clk connections in the bank"""
|
||||
#wl_en only used in the wordline driver.
|
||||
# wl_en only used in the wordline driver.
|
||||
return self.port_address.wordline_driver.get_wl_en_cin()
|
||||
|
||||
def get_w_en_cin(self):
|
||||
"""Get the relative capacitance of all the clk connections in the bank"""
|
||||
#wl_en only used in the wordline driver.
|
||||
port = self.write_ports[0]
|
||||
# wl_en only used in the wordline driver.
|
||||
port = self.write_ports[0]
|
||||
return self.port_data[port].write_driver.get_w_en_cin()
|
||||
|
||||
def get_clk_bar_cin(self):
|
||||
"""Get the relative capacitance of all the clk_bar connections in the bank"""
|
||||
#Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array.
|
||||
# Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array.
|
||||
|
||||
#Precharges are the all the same in Mulitport, one is picked
|
||||
# Precharges are the all the same in Mulitport, one is picked
|
||||
port = self.read_ports[0]
|
||||
return self.port_data[port].precharge_array.get_en_cin()
|
||||
|
||||
def get_sen_cin(self):
|
||||
"""Get the relative capacitance of all the sense amp enable connections in the bank"""
|
||||
#Current bank only uses sen as an enable for the sense amps.
|
||||
port = self.read_ports[0]
|
||||
# Current bank only uses sen as an enable for the sense amps.
|
||||
port = self.read_ports[0]
|
||||
return self.port_data[port].sense_amp_array.get_en_cin()
|
||||
|
||||
def graph_exclude_precharge(self):
|
||||
|
|
@ -1005,5 +993,7 @@ class bank(design.design):
|
|||
|
||||
def get_cell_name(self, inst_name, row, col):
|
||||
"""Gets the spice name of the target bitcell."""
|
||||
return self.bitcell_array_inst.mod.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col)
|
||||
return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name,
|
||||
row,
|
||||
col)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ class hierarchical_decoder(design.design):
|
|||
def __init__(self, name, rows):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.NAND_FORMAT = "DEC_NAND_{0}"
|
||||
self.INV_FORMAT = "DEC_INV_{0}"
|
||||
self.AND_FORMAT = "DEC_AND_{0}"
|
||||
|
||||
self.pre2x4_inst = []
|
||||
self.pre3x8_inst = []
|
||||
|
|
@ -58,12 +57,12 @@ class hierarchical_decoder(design.design):
|
|||
self.inv = factory.create(module_type="pinv",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.inv)
|
||||
self.nand2 = factory.create(module_type="pnand2",
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and2)
|
||||
self.and3 = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.nand2)
|
||||
self.nand3 = factory.create(module_type="pnand3",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.nand3)
|
||||
self.add_mod(self.and3)
|
||||
|
||||
self.add_decoders()
|
||||
|
||||
|
|
@ -77,27 +76,27 @@ class hierarchical_decoder(design.design):
|
|||
height=self.cell_height)
|
||||
self.add_mod(self.pre3_8)
|
||||
|
||||
def determine_predecodes(self,num_inputs):
|
||||
def determine_predecodes(self, num_inputs):
|
||||
""" Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
|
||||
needed based on the number of inputs """
|
||||
if (num_inputs == 2):
|
||||
return (1,0)
|
||||
return (1, 0)
|
||||
elif (num_inputs == 3):
|
||||
return(0,1)
|
||||
return(0, 1)
|
||||
elif (num_inputs == 4):
|
||||
return(2,0)
|
||||
return(2, 0)
|
||||
elif (num_inputs == 5):
|
||||
return(1,1)
|
||||
return(1, 1)
|
||||
elif (num_inputs == 6):
|
||||
return(3,0)
|
||||
return(3, 0)
|
||||
elif (num_inputs == 7):
|
||||
return(2,1)
|
||||
return(2, 1)
|
||||
elif (num_inputs == 8):
|
||||
return(1,2)
|
||||
return(1, 2)
|
||||
elif (num_inputs == 9):
|
||||
return(0,3)
|
||||
return(0, 3)
|
||||
else:
|
||||
debug.error("Invalid number of inputs for hierarchical decoder",-1)
|
||||
debug.error("Invalid number of inputs for hierarchical decoder", -1)
|
||||
|
||||
def setup_netlist_constants(self):
|
||||
self.predec_groups = [] # This array is a 2D array.
|
||||
|
|
@ -122,35 +121,35 @@ class hierarchical_decoder(design.design):
|
|||
index = index + 1
|
||||
self.predec_groups.append(lines)
|
||||
|
||||
|
||||
def setup_layout_constants(self):
|
||||
""" Calculate the overall dimensions of the hierarchical decoder """
|
||||
|
||||
# If we have 4 or fewer rows, the predecoder is the decoder itself
|
||||
if self.num_inputs>=4:
|
||||
self.total_number_of_predecoder_outputs = 4*self.no_of_pre2x4 + 8*self.no_of_pre3x8
|
||||
self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8
|
||||
else:
|
||||
self.total_number_of_predecoder_outputs = 0
|
||||
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),-1)
|
||||
self.total_number_of_predecoder_outputs = 0
|
||||
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),
|
||||
-1)
|
||||
|
||||
# Calculates height and width of pre-decoder,
|
||||
if self.no_of_pre3x8 > 0:
|
||||
self.predecoder_width = self.pre3_8.width
|
||||
self.predecoder_width = self.pre3_8.width
|
||||
else:
|
||||
self.predecoder_width = self.pre2_4.width
|
||||
|
||||
self.predecoder_height = self.pre2_4.height*self.no_of_pre2x4 + self.pre3_8.height*self.no_of_pre3x8
|
||||
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
|
||||
|
||||
# Calculates height and width of row-decoder
|
||||
# Calculates height and width of row-decoder
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
nand_width = self.nand2.width
|
||||
nand_width = self.and2.width
|
||||
else:
|
||||
nand_width = self.nand3.width
|
||||
self.internal_routing_width = self.m2_pitch*self.total_number_of_predecoder_outputs
|
||||
nand_width = self.and3.width
|
||||
self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs
|
||||
self.row_decoder_height = self.inv.height * self.rows
|
||||
|
||||
self.input_routing_width = (self.num_inputs+1) * self.m2_pitch
|
||||
# Calculates height and width of hierarchical decoder
|
||||
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
||||
# Calculates height and width of hierarchical decoder
|
||||
self.height = self.row_decoder_height
|
||||
self.width = self.input_routing_width + self.predecoder_width \
|
||||
+ self.internal_routing_width + nand_width + self.inv.width
|
||||
|
|
@ -158,7 +157,7 @@ class hierarchical_decoder(design.design):
|
|||
def route_input_rails(self):
|
||||
""" Create input rails for the predecoders """
|
||||
# inputs should be as high as the decoders
|
||||
input_height = self.no_of_pre2x4*self.pre2_4.height + self.no_of_pre3x8*self.pre3_8.height
|
||||
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
|
||||
|
||||
# Find the left-most predecoder
|
||||
min_x = 0
|
||||
|
|
@ -166,7 +165,7 @@ class hierarchical_decoder(design.design):
|
|||
min_x = min(min_x, -self.pre2_4.width)
|
||||
if self.no_of_pre3x8 > 0:
|
||||
min_x = min(min_x, -self.pre3_8.width)
|
||||
input_offset=vector(min_x - self.input_routing_width,0)
|
||||
input_offset=vector(min_x - self.input_routing_width, 0)
|
||||
|
||||
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
||||
|
|
@ -177,7 +176,6 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
self.route_input_to_predecodes()
|
||||
|
||||
|
||||
def route_input_to_predecodes(self):
|
||||
""" Route the vertical input rail to the predecoders """
|
||||
for pre_num in range(self.no_of_pre2x4):
|
||||
|
|
@ -191,11 +189,10 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||
|
||||
self.route_input_rail(decoder_offset, input_offset)
|
||||
|
||||
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
for i in range(3):
|
||||
|
|
@ -208,11 +205,10 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||
|
||||
self.route_input_rail(decoder_offset, input_offset)
|
||||
|
||||
self.route_input_rail(decoder_offset, input_offset)
|
||||
|
||||
def route_input_rail(self, input_offset, output_offset):
|
||||
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
|
||||
|
|
@ -222,7 +218,6 @@ class hierarchical_decoder(design.design):
|
|||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=output_offset)
|
||||
self.add_path(("m3"), [input_offset, output_offset])
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the module pins """
|
||||
|
|
@ -235,7 +230,6 @@ class hierarchical_decoder(design.design):
|
|||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
|
||||
def create_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
|
||||
|
|
@ -245,7 +239,7 @@ class hierarchical_decoder(design.design):
|
|||
for i in range(self.no_of_pre3x8):
|
||||
self.create_pre3x8(i)
|
||||
|
||||
def create_pre2x4(self,num):
|
||||
def create_pre2x4(self, num):
|
||||
""" Add a 2x4 predecoder to the left of the origin """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
|
|
@ -265,8 +259,7 @@ class hierarchical_decoder(design.design):
|
|||
mod=self.pre2_4))
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def create_pre3x8(self,num):
|
||||
def create_pre3x8(self, num):
|
||||
""" Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||
# If we had 2x4 predecodes, those are used as the lower
|
||||
# decode output bits
|
||||
|
|
@ -280,11 +273,10 @@ class hierarchical_decoder(design.design):
|
|||
pins.append("out_{0}".format(output_index + out_index_offset))
|
||||
pins.extend(["vdd", "gnd"])
|
||||
|
||||
self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num),
|
||||
self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num),
|
||||
mod=self.pre3_8))
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def place_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
|
||||
|
|
@ -294,196 +286,131 @@ class hierarchical_decoder(design.design):
|
|||
for i in range(self.no_of_pre3x8):
|
||||
self.place_pre3x8(i)
|
||||
|
||||
def place_pre2x4(self,num):
|
||||
def place_pre2x4(self, num):
|
||||
""" Place 2x4 predecoder to the left of the origin """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
base = vector(-self.pre2_4.width,0)
|
||||
base = vector(-self.pre2_4.width, 0)
|
||||
else:
|
||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
|
||||
self.pre2x4_inst[num].place(base)
|
||||
|
||||
def place_pre3x8(self,num):
|
||||
def place_pre3x8(self, num):
|
||||
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||
if (self.num_inputs == 3):
|
||||
offset = vector(-self.pre_3_8.width,0)
|
||||
mirror ="R0"
|
||||
offset = vector(-self.pre_3_8.width, 0)
|
||||
mirror = "R0"
|
||||
else:
|
||||
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
|
||||
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
|
||||
offset = vector(-self.pre3_8.width, height)
|
||||
|
||||
self.pre3x8_inst[num].place(offset)
|
||||
|
||||
def create_row_decoder(self):
|
||||
""" Create the row-decoder by placing NAND2/NAND3 and Inverters
|
||||
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
||||
and add the primary decoder output pins. """
|
||||
if (self.num_inputs >= 4):
|
||||
self.create_decoder_nand_array()
|
||||
self.create_decoder_inv_array()
|
||||
self.create_decoder_and_array()
|
||||
|
||||
def create_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
def create_decoder_and_array(self):
|
||||
""" Add a column of AND gates for final decode """
|
||||
|
||||
self.nand_inst = []
|
||||
self.and_inst = []
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs <5.
|
||||
# Row Decoder AND GATE array for address inputs <5.
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
row = len(self.predec_groups[0])*j + i
|
||||
row = len(self.predec_groups[0]) * j + i
|
||||
if (row < self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand2))
|
||||
name = self.AND_FORMAT.format(row)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and2))
|
||||
pins =["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"Z_{0}".format(row),
|
||||
"decode_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs >5.
|
||||
# Row Decoder AND GATE array for address inputs >5.
|
||||
elif (self.num_inputs > 5):
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
for k in range(len(self.predec_groups[2])):
|
||||
row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \
|
||||
+ len(self.predec_groups[0])*j + i
|
||||
row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
|
||||
+ len(self.predec_groups[0]) * j + i
|
||||
|
||||
if (row < self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand3))
|
||||
name = self.AND_FORMAT.format(row)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and3))
|
||||
|
||||
pins = ["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||
"Z_{0}".format(row),
|
||||
"decode_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def create_decoder_inv_array(self):
|
||||
"""
|
||||
Add a column of INV gates for the decoder.
|
||||
"""
|
||||
|
||||
self.inv_inst = []
|
||||
for row in range(self.rows):
|
||||
name = self.INV_FORMAT.format(row)
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.connect_inst(args=["Z_{0}".format(row),
|
||||
"decode_{0}".format(row),
|
||||
"vdd", "gnd"])
|
||||
|
||||
|
||||
def place_decoder_inv_array(self):
|
||||
"""
|
||||
Place the column of INV gates for the decoder above the predecoders
|
||||
and to the right of the NAND decoders.
|
||||
"""
|
||||
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
x_off = self.internal_routing_width + self.nand2.width
|
||||
else:
|
||||
x_off = self.internal_routing_width + self.nand3.width
|
||||
|
||||
for row in range(self.rows):
|
||||
if (row % 2 == 0):
|
||||
inv_row_height = self.inv.height * row
|
||||
mirror = "R0"
|
||||
y_dir = 1
|
||||
else:
|
||||
inv_row_height = self.inv.height * (row + 1)
|
||||
mirror = "MX"
|
||||
y_dir = -1
|
||||
y_off = inv_row_height
|
||||
offset = vector(x_off,y_off)
|
||||
self.inv_inst[row].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def place_row_decoder(self):
|
||||
"""
|
||||
Place the row-decoder by placing NAND2/NAND3 and Inverters
|
||||
and add the primary decoder output pins.
|
||||
"""
|
||||
Place the row-decoder by placing AND2/AND3 and Inverters
|
||||
and add the primary decoder output pins.
|
||||
"""
|
||||
if (self.num_inputs >= 4):
|
||||
self.place_decoder_nand_array()
|
||||
self.place_decoder_inv_array()
|
||||
self.place_decoder_and_array()
|
||||
self.route_decoder()
|
||||
|
||||
|
||||
def place_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
def place_decoder_and_array(self):
|
||||
""" Add a column of AND gates for final decode """
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs <5.
|
||||
# Row Decoder AND GATE array for address inputs <5.
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
self.place_nand_array(nand_mod=self.nand2)
|
||||
self.place_and_array(and_mod=self.and2)
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs >5.
|
||||
# Row Decoder AND GATE array for address inputs >5.
|
||||
# FIXME: why this correct offset?)
|
||||
elif (self.num_inputs > 5):
|
||||
self.place_nand_array(nand_mod=self.nand3)
|
||||
self.place_and_array(and_mod=self.and3)
|
||||
|
||||
def place_nand_array(self, nand_mod):
|
||||
""" Add a column of NAND gates for the decoder above the predecoders."""
|
||||
def place_and_array(self, and_mod):
|
||||
""" Add a column of AND gates for the decoder above the predecoders."""
|
||||
|
||||
for row in range(self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
if ((row % 2) == 0):
|
||||
y_off = nand_mod.height*row
|
||||
y_dir = 1
|
||||
y_off = and_mod.height * row
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = nand_mod.height*(row + 1)
|
||||
y_dir = -1
|
||||
y_off = and_mod.height * (row + 1)
|
||||
mirror = "MX"
|
||||
|
||||
self.nand_inst[row].place(offset=[self.internal_routing_width, y_off],
|
||||
mirror=mirror)
|
||||
|
||||
|
||||
|
||||
self.and_inst[row].place(offset=[self.internal_routing_width, y_off],
|
||||
mirror=mirror)
|
||||
|
||||
def route_decoder(self):
|
||||
""" Route the nand to inverter in the decoder and add the pins. """
|
||||
""" Add the pins. """
|
||||
|
||||
for row in range(self.rows):
|
||||
|
||||
# route nand output to output inv input
|
||||
zr_pos = self.nand_inst[row].get_pin("Z").rc()
|
||||
al_pos = self.inv_inst[row].get_pin("A").lc()
|
||||
# ensure the bend is in the middle
|
||||
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
||||
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
||||
z_pin = self.inv_inst[row].get_pin("Z")
|
||||
z_pin = self.and_inst[row].get_pin("Z")
|
||||
self.add_layout_pin(text="decode_{0}".format(row),
|
||||
layer="m1",
|
||||
offset=z_pin.ll(),
|
||||
width=z_pin.width(),
|
||||
height=z_pin.height())
|
||||
|
||||
|
||||
|
||||
def route_predecode_rails(self):
|
||||
""" Creates vertical metal 2 rails to connect predecoder and decoder stages."""
|
||||
|
||||
# This is not needed for inputs <4 since they have no pre/decode stages.
|
||||
if (self.num_inputs >= 4):
|
||||
input_offset = vector(0.5*self.m2_width,0)
|
||||
input_offset = vector(0.5 * self.m2_width, 0)
|
||||
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||
self.predecode_rails = self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=self.height)
|
||||
|
||||
|
||||
self.route_rails_to_predecodes()
|
||||
self.route_rails_to_decoder()
|
||||
|
|
@ -497,8 +424,7 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
||||
out_name = "out_{}".format(i)
|
||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
# FIXME: convert to connect_bus
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
|
|
@ -506,17 +432,15 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
||||
out_name = "out_{}".format(i)
|
||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
|
||||
|
||||
def route_rails_to_decoder(self):
|
||||
""" Use the self.predec_groups to determine the connections to the decoder NAND gates.
|
||||
Inputs of NAND2/NAND3 gates come from different groups.
|
||||
""" Use the self.predec_groups to determine the connections to the decoder AND gates.
|
||||
Inputs of AND2/AND3 gates come from different groups.
|
||||
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
|
||||
[8,9,10,11,12,13,14,15] ] the first NAND3 inputs are connected to
|
||||
[0,4,8] and second NAND3 is connected to [0,4,9] ........... and the
|
||||
128th NAND3 is connected to [3,7,15]
|
||||
[8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
|
||||
[0,4,8] and second AND3 is connected to [0,4,9] ........... and the
|
||||
128th AND3 is connected to [3,7,15]
|
||||
"""
|
||||
row_index = 0
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
|
|
@ -525,9 +449,9 @@ class hierarchical_decoder(design.design):
|
|||
# FIXME: convert to connect_bus?
|
||||
if (row_index < self.rows):
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
|
||||
row_index = row_index + 1
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
|
|
@ -536,61 +460,48 @@ class hierarchical_decoder(design.design):
|
|||
for index_A in self.predec_groups[0]:
|
||||
# FIXME: convert to connect_bus?
|
||||
if (row_index < self.rows):
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C"))
|
||||
row_index = row_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
|
||||
# The vias will be placed in the center and right of the cells, respectively.
|
||||
xoffset = self.nand_inst[0].rx()
|
||||
for num in range(0,self.rows):
|
||||
xoffset = self.and_inst[0].rx()
|
||||
for num in range(0, self.rows):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# The nand and inv are the same height rows...
|
||||
supply_pin = self.nand_inst[num].get_pin(pin_name)
|
||||
supply_pin = self.and_inst[num].get_pin(pin_name)
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin_pos)
|
||||
|
||||
# Make a redundant rail too
|
||||
for num in range(0,self.rows,2):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
start = self.nand_inst[num].get_pin(pin_name).lc()
|
||||
end = self.inv_inst[num].get_pin(pin_name).rc()
|
||||
mid = (start+end).scale(0.5,0.5)
|
||||
self.add_rect_center(layer="m1",
|
||||
offset=mid,
|
||||
width=end.x-start.x)
|
||||
|
||||
|
||||
# Copy the pins from the predecoders
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(pre, "vdd")
|
||||
self.copy_layout_pin(pre, "gnd")
|
||||
|
||||
|
||||
def route_predecode_rail(self, rail_name, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y)
|
||||
self.add_path("m1", [rail_pos, pin.lc()])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
def route_predecode_rail_m3(self, rail_name, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||
# It would be fixed with a channel router.
|
||||
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y)
|
||||
mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pin.center())
|
||||
self.add_wire(("m3","via2","m2"), [rail_pos, mid_point, pin.uc()])
|
||||
self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=rail_pos)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,61 +32,58 @@ class hierarchical_predecode(design.design):
|
|||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the INV and NAND gate modules """
|
||||
""" Add the INV and AND gate modules """
|
||||
|
||||
self.inv = factory.create(module_type="pinv",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.add_nand(self.number_of_inputs)
|
||||
self.add_mod(self.nand)
|
||||
self.add_and(self.number_of_inputs)
|
||||
self.add_mod(self.and_mod)
|
||||
|
||||
def add_nand(self, inputs):
|
||||
def add_and(self, inputs):
|
||||
""" Create the NAND for the predecode input stage """
|
||||
if inputs==2:
|
||||
self.nand = factory.create(module_type="pnand2",
|
||||
height=self.cell_height)
|
||||
self.and_mod = factory.create(module_type="pand2",
|
||||
height=self.cell_height)
|
||||
elif inputs==3:
|
||||
self.nand = factory.create(module_type="pnand3",
|
||||
height=self.cell_height)
|
||||
self.and_mod = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
else:
|
||||
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
|
||||
|
||||
def setup_layout_constraints(self):
|
||||
|
||||
self.height = self.number_of_outputs * self.nand.height
|
||||
self.height = self.number_of_outputs * self.and_mod.height
|
||||
|
||||
# x offset for input inverters
|
||||
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
|
||||
|
||||
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
||||
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
|
||||
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
||||
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
|
||||
|
||||
# x offset to output inverters
|
||||
self.x_off_inv_2 = self.x_off_nand + self.nand.width
|
||||
|
||||
# Height width are computed
|
||||
self.width = self.x_off_inv_2 + self.inv.width
|
||||
self.width = self.x_off_and + self.and_mod.width
|
||||
|
||||
def route_rails(self):
|
||||
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
||||
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
offset = vector(0.5*self.m2_width,2*self.m1_width)
|
||||
offset = vector(0.5 * self.m2_width, self.m1_pitch)
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=offset,
|
||||
names=input_names,
|
||||
length=self.height - 2*self.m1_width)
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
|
||||
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
decode_names = invert_names + non_invert_names
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 2*self.m1_width)
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m1_pitch)
|
||||
self.decode_rails = self.create_vertical_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=offset,
|
||||
names=decode_names,
|
||||
length=self.height - 2*self.m1_width)
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
|
||||
def create_input_inverters(self):
|
||||
""" Create the input inverters to invert input signals for the decode stage. """
|
||||
|
|
@ -112,62 +109,35 @@ class hierarchical_predecode(design.design):
|
|||
self.in_inst[inv_num].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def create_output_inverters(self):
|
||||
""" Create inverters for the inverted output decode signals. """
|
||||
self.inv_inst = []
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
name = "pre_nand_inv_{}".format(inv_num)
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.connect_inst(["Z_{}".format(inv_num),
|
||||
"out_{}".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
def create_and_array(self, connections):
|
||||
""" Create the AND stage for the decodes """
|
||||
self.and_inst = []
|
||||
for and_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
|
||||
name = "Xpre{0}_and_{1}".format(inout, and_input)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and_mod))
|
||||
self.connect_inst(connections[and_input])
|
||||
|
||||
|
||||
def place_output_inverters(self):
|
||||
""" Place inverters for the inverted output decode signals. """
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
if (inv_num % 2 == 0):
|
||||
y_off = inv_num * self.inv.height
|
||||
def place_and_array(self):
|
||||
""" Place the AND stage for the decodes """
|
||||
for and_input in range(self.number_of_outputs):
|
||||
# inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
|
||||
if (and_input % 2 == 0):
|
||||
y_off = and_input * self.and_mod.height
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off =(inv_num + 1)*self.inv.height
|
||||
y_off = (and_input + 1) * self.and_mod.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_inv_2, y_off)
|
||||
self.inv_inst[inv_num].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def create_nand_array(self,connections):
|
||||
""" Create the NAND stage for the decodes """
|
||||
self.nand_inst = []
|
||||
for nand_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
|
||||
name = "Xpre{0}_nand_{1}".format(inout,nand_input)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand))
|
||||
self.connect_inst(connections[nand_input])
|
||||
|
||||
|
||||
def place_nand_array(self):
|
||||
""" Place the NAND stage for the decodes """
|
||||
for nand_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
|
||||
if (nand_input % 2 == 0):
|
||||
y_off = nand_input * self.inv.height
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = (nand_input + 1) * self.inv.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_nand, y_off)
|
||||
self.nand_inst[nand_input].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
offset = vector(self.x_off_and, y_off)
|
||||
self.and_inst[and_input].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def route(self):
|
||||
self.route_input_inverters()
|
||||
self.route_inputs_to_rails()
|
||||
self.route_nand_to_rails()
|
||||
self.route_output_inverters()
|
||||
self.route_and_to_rails()
|
||||
self.route_output_and()
|
||||
self.route_vdd_gnd()
|
||||
|
||||
def route_inputs_to_rails(self):
|
||||
|
|
@ -175,39 +145,30 @@ class hierarchical_predecode(design.design):
|
|||
for num in range(self.number_of_inputs):
|
||||
# route one signal next to each vdd/gnd rail since this is
|
||||
# typically where the p/n devices are and there are no
|
||||
# pins in the nand gates.
|
||||
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
|
||||
in_pin = "in_{}".format(num)
|
||||
# pins in the and gates.
|
||||
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
|
||||
in_pin = "in_{}".format(num)
|
||||
a_pin = "A_{}".format(num)
|
||||
in_pos = vector(self.input_rails[in_pin].x,y_offset)
|
||||
a_pos = vector(self.decode_rails[a_pin].x,y_offset)
|
||||
self.add_path("m1",[in_pos, a_pos])
|
||||
self.add_via_center(layers = self.m1_stack,
|
||||
in_pos = vector(self.input_rails[in_pin].x, y_offset)
|
||||
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
|
||||
self.add_path("m1", [in_pos, a_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=[self.input_rails[in_pin].x, y_offset])
|
||||
self.add_via_center(layers = self.m1_stack,
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=[self.decode_rails[a_pin].x, y_offset])
|
||||
|
||||
def route_output_inverters(self):
|
||||
def route_output_and(self):
|
||||
"""
|
||||
Route all conections of the outputs inverters
|
||||
Route all conections of the outputs and gates
|
||||
"""
|
||||
for num in range(self.number_of_outputs):
|
||||
|
||||
# route nand output to output inv input
|
||||
zr_pos = self.nand_inst[num].get_pin("Z").rc()
|
||||
al_pos = self.inv_inst[num].get_pin("A").lc()
|
||||
# ensure the bend is in the middle
|
||||
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
||||
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
||||
z_pin = self.inv_inst[num].get_pin("Z")
|
||||
z_pin = self.and_inst[num].get_pin("Z")
|
||||
self.add_layout_pin(text="out_{}".format(num),
|
||||
layer="m1",
|
||||
offset=z_pin.ll(),
|
||||
height=z_pin.height(),
|
||||
width=z_pin.width())
|
||||
|
||||
|
||||
def route_input_inverters(self):
|
||||
"""
|
||||
|
|
@ -219,7 +180,7 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
#add output so that it is just below the vdd or gnd rail
|
||||
# since this is where the p/n devices are and there are no
|
||||
# pins in the nand gates.
|
||||
# pins in the and gates.
|
||||
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
|
||||
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
|
||||
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
|
||||
|
|
@ -236,13 +197,12 @@ class hierarchical_predecode(design.design):
|
|||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=in_pos)
|
||||
|
||||
|
||||
def route_nand_to_rails(self):
|
||||
# This 2D array defines the connection mapping
|
||||
nand_input_line_combination = self.get_nand_input_line_combination()
|
||||
def route_and_to_rails(self):
|
||||
# This 2D array defines the connection mapping
|
||||
and_input_line_combination = self.get_and_input_line_combination()
|
||||
for k in range(self.number_of_outputs):
|
||||
# create x offset list
|
||||
index_lst= nand_input_line_combination[k]
|
||||
# create x offset list
|
||||
index_lst= and_input_line_combination[k]
|
||||
|
||||
if self.number_of_inputs == 2:
|
||||
gate_lst = ["A","B"]
|
||||
|
|
@ -251,35 +211,32 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
# this will connect pins A,B or A,B,C
|
||||
for rail_pin,gate_pin in zip(index_lst,gate_lst):
|
||||
pin_pos = self.nand_inst[k].get_pin(gate_pin).lc()
|
||||
pin_pos = self.and_inst[k].get_pin(gate_pin).lc()
|
||||
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
||||
self.add_path("m1", [rail_pos, pin_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
|
||||
# Find the x offsets for where the vias/pins should be placed
|
||||
in_xoffset = self.in_inst[0].rx() + self.m1_space
|
||||
out_xoffset = self.inv_inst[0].lx() - self.m1_space
|
||||
for num in range(0,self.number_of_outputs):
|
||||
# out_xoffset = self.and_inst[0].cx() + self.m1_space
|
||||
for num in range(0, self.number_of_outputs):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# Route both supplies
|
||||
for n in ["vdd", "gnd"]:
|
||||
nand_pin = self.nand_inst[num].get_pin(n)
|
||||
supply_offset = nand_pin.ll().scale(0,1)
|
||||
and_pin = self.and_inst[num].get_pin(n)
|
||||
supply_offset = and_pin.ll().scale(0, 1)
|
||||
self.add_rect(layer="m1",
|
||||
offset=supply_offset,
|
||||
width=self.inv_inst[num].rx())
|
||||
width=self.and_inst[num].rx())
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [in_xoffset, out_xoffset]:
|
||||
pin_pos = vector(xoffset, nand_pin.cy())
|
||||
for xoffset in [in_xoffset]:
|
||||
pin_pos = vector(xoffset, and_pin.cy())
|
||||
self.add_power_pin(n, pin_pos)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,33 +27,31 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "Z_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "Z_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "Z_3", "vdd", "gnd"]]
|
||||
self.create_nand_array(connections)
|
||||
connections =[["inbar_0", "inbar_1", "out_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "out_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "out_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "out_3", "vdd", "gnd"]]
|
||||
self.create_and_array(connections)
|
||||
|
||||
def create_layout(self):
|
||||
""" The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
4) a set of AND gates for inversion
|
||||
"""
|
||||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
self.place_nand_array()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
""" These are the decoder connections of the NAND gates to the A,B pins """
|
||||
def get_and_input_line_combination(self):
|
||||
""" These are the decoder connections of the AND gates to the A,B pins """
|
||||
combination = [["Abar_0", "Abar_1"],
|
||||
["A_0", "Abar_1"],
|
||||
["Abar_0", "A_1"],
|
||||
["A_0", "A_1"]]
|
||||
return combination
|
||||
return combination
|
||||
|
|
|
|||
|
|
@ -20,26 +20,25 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
hierarchical_predecode.__init__(self, name, 3, height)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "inbar_2", "Z_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "inbar_2", "Z_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "inbar_2", "Z_3", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "in_2", "Z_4", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "in_2", "Z_5", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "in_2", "Z_6", "vdd", "gnd"],
|
||||
["in_0", "in_1", "in_2", "Z_7", "vdd", "gnd"]]
|
||||
self.create_nand_array(connections)
|
||||
connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
|
||||
["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
|
||||
self.create_and_array(connections)
|
||||
|
||||
def create_layout(self):
|
||||
"""
|
||||
"""
|
||||
The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
|
|
@ -49,20 +48,19 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
self.place_nand_array()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
def get_and_input_line_combination(self):
|
||||
""" These are the decoder connections of the NAND gates to the A,B,C pins """
|
||||
combination = [["Abar_0", "Abar_1", "Abar_2"],
|
||||
["A_0", "Abar_1", "Abar_2"],
|
||||
["Abar_0", "A_1", "Abar_2"],
|
||||
["A_0", "A_1", "Abar_2"],
|
||||
["Abar_0", "Abar_1", "A_2"],
|
||||
["Abar_0", "Abar_1", "A_2"],
|
||||
["A_0", "Abar_1", "A_2"],
|
||||
["Abar_0", "A_1", "A_2"],
|
||||
["A_0", "A_1", "A_2"]]
|
||||
return combination
|
||||
return combination
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2019 Regents of the University of California
|
||||
# Copyright (c) 2016-2019 Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
from tech import drc, parameter
|
||||
from tech import drc
|
||||
import debug
|
||||
import design
|
||||
from sram_factory import factory
|
||||
from collections import namedtuple
|
||||
from vector import vector
|
||||
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class port_data(design.design):
|
||||
"""
|
||||
Create the data port (column mux, sense amps, write driver, etc.) for the given port number.
|
||||
|
|
@ -24,18 +23,21 @@ class port_data(design.design):
|
|||
sram_config.set_local_config(self)
|
||||
self.port = port
|
||||
if self.write_size is not None:
|
||||
self.num_wmasks = int(self.word_size/self.write_size)
|
||||
self.num_wmasks = int(self.word_size / self.write_size)
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
if name == "":
|
||||
name = "port_data_{0}".format(self.port)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create data port of size {0} with {1} words per row".format(self.word_size,self.words_per_row))
|
||||
debug.info(2,
|
||||
"create data port of size {0} with {1} words per row".format(self.word_size,
|
||||
self.words_per_row))
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
|
||||
debug.check(len(self.all_ports)<=2,
|
||||
"Bank layout cannot handle more than two ports.")
|
||||
self.create_layout()
|
||||
self.add_boundary()
|
||||
|
||||
|
|
@ -93,8 +95,6 @@ class port_data(design.design):
|
|||
else:
|
||||
self.column_mux_array_inst = None
|
||||
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
self.compute_instance_offsets()
|
||||
self.place_instances()
|
||||
|
|
@ -104,33 +104,32 @@ class port_data(design.design):
|
|||
def add_pins(self):
|
||||
""" Adding pins for port address module"""
|
||||
|
||||
self.add_pin("rbl_bl","INOUT")
|
||||
self.add_pin("rbl_br","INOUT")
|
||||
self.add_pin("rbl_bl", "INOUT")
|
||||
self.add_pin("rbl_br", "INOUT")
|
||||
for bit in range(self.num_cols):
|
||||
bl_name = self.get_bl_name(self.port)
|
||||
br_name = self.get_br_name(self.port)
|
||||
self.add_pin("{0}_{1}".format(bl_name, bit),"INOUT")
|
||||
self.add_pin("{0}_{1}".format(br_name, bit),"INOUT")
|
||||
self.add_pin("{0}_{1}".format(bl_name, bit), "INOUT")
|
||||
self.add_pin("{0}_{1}".format(br_name, bit), "INOUT")
|
||||
if self.port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("dout_{}".format(bit),"OUTPUT")
|
||||
self.add_pin("dout_{}".format(bit), "OUTPUT")
|
||||
if self.port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("din_{}".format(bit),"INPUT")
|
||||
self.add_pin("din_{}".format(bit), "INPUT")
|
||||
# Will be empty if no col addr lines
|
||||
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
||||
for pin_name in sel_names:
|
||||
self.add_pin(pin_name,"INPUT")
|
||||
self.add_pin(pin_name, "INPUT")
|
||||
if self.port in self.read_ports:
|
||||
self.add_pin("s_en", "INPUT")
|
||||
self.add_pin("p_en_bar", "INPUT")
|
||||
if self.port in self.write_ports:
|
||||
self.add_pin("w_en", "INPUT")
|
||||
for bit in range(self.num_wmasks):
|
||||
self.add_pin("bank_wmask_{}".format(bit),"INPUT")
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.add_pin("bank_wmask_{}".format(bit), "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def route_layout(self):
|
||||
""" Create routing among the modules """
|
||||
|
|
@ -172,8 +171,8 @@ class port_data(design.design):
|
|||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
|
||||
for inst in self.insts:
|
||||
self.copy_power_pins(inst,"vdd")
|
||||
self.copy_power_pins(inst,"gnd")
|
||||
self.copy_power_pins(inst, "vdd")
|
||||
self.copy_power_pins(inst, "gnd")
|
||||
|
||||
def add_modules(self):
|
||||
|
||||
|
|
@ -193,7 +192,6 @@ class port_data(design.design):
|
|||
else:
|
||||
self.sense_amp_array = None
|
||||
|
||||
|
||||
if self.col_addr_size > 0:
|
||||
self.column_mux_array = factory.create(module_type="column_mux_array",
|
||||
columns=self.num_cols,
|
||||
|
|
@ -204,7 +202,6 @@ class port_data(design.design):
|
|||
else:
|
||||
self.column_mux_array = None
|
||||
|
||||
|
||||
if self.port in self.write_ports:
|
||||
self.write_driver_array = factory.create(module_type="write_driver_array",
|
||||
columns=self.num_cols,
|
||||
|
|
@ -234,29 +231,27 @@ class port_data(design.design):
|
|||
else:
|
||||
self.num_col_addr_lines = 0
|
||||
|
||||
|
||||
# A space for wells or jogging m2 between modules
|
||||
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3*self.m2_pitch)
|
||||
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3 * self.m2_pitch)
|
||||
|
||||
|
||||
# create arrays of bitline and bitline_bar names for read, write, or all ports
|
||||
# create arrays of bitline and bitline_bar names for read,
|
||||
# write, or all ports
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bl_names = self.bitcell.get_all_bl_names()
|
||||
self.br_names = self.bitcell.get_all_br_names()
|
||||
self.wl_names = self.bitcell.get_all_wl_names()
|
||||
# used for bl/br names
|
||||
self.precharge = factory.create(module_type="precharge",
|
||||
bitcell_bl = self.bl_names[0],
|
||||
bitcell_br = self.br_names[0])
|
||||
bitcell_bl=self.bl_names[0],
|
||||
bitcell_br=self.br_names[0])
|
||||
# We create a dummy here to get bl/br names to add those pins to this
|
||||
# module, which happens before we create the real precharge_array
|
||||
self.precharge_array = factory.create(module_type="precharge_array",
|
||||
columns=self.num_cols + 1,
|
||||
bitcell_bl=self.bl_names[self.port],
|
||||
bitcell_br=self.br_names[self.port])
|
||||
|
||||
|
||||
columns=self.num_cols + 1,
|
||||
bitcell_bl=self.bl_names[self.port],
|
||||
bitcell_br=self.br_names[self.port])
|
||||
|
||||
def create_precharge_array(self):
|
||||
""" Creating Precharge """
|
||||
|
|
@ -285,13 +280,11 @@ class port_data(design.design):
|
|||
temp.extend(["p_en_bar", "vdd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_precharge_array(self, offset):
|
||||
""" Placing Precharge """
|
||||
|
||||
self.precharge_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_column_mux_array(self):
|
||||
""" Creating Column Mux when words_per_row > 1 . """
|
||||
|
||||
|
|
@ -314,7 +307,6 @@ class port_data(design.design):
|
|||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_column_mux_array(self, offset):
|
||||
""" Placing Column Mux when words_per_row > 1 . """
|
||||
if self.col_addr_size == 0:
|
||||
|
|
@ -322,7 +314,6 @@ class port_data(design.design):
|
|||
|
||||
self.column_mux_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_sense_amp_array(self):
|
||||
""" Creating Sense amp """
|
||||
self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port),
|
||||
|
|
@ -340,16 +331,13 @@ class port_data(design.design):
|
|||
temp.append("{0}_out_{1}".format(bl_name, bit))
|
||||
temp.append("{0}_out_{1}".format(br_name, bit))
|
||||
|
||||
|
||||
temp.extend(["s_en", "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_sense_amp_array(self, offset):
|
||||
""" Placing Sense amp """
|
||||
self.sense_amp_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_write_driver_array(self):
|
||||
""" Creating Write Driver """
|
||||
self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port),
|
||||
|
|
@ -378,12 +366,10 @@ class port_data(design.design):
|
|||
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_write_driver_array(self, offset):
|
||||
""" Placing Write Driver """
|
||||
self.write_driver_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_write_mask_and_array(self):
|
||||
""" Creating Write Mask AND Array """
|
||||
self.write_mask_and_array_inst = self.add_inst(name="write_mask_and_array{}".format(self.port),
|
||||
|
|
@ -398,12 +384,10 @@ class port_data(design.design):
|
|||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_write_mask_and_array(self, offset):
|
||||
""" Placing Write Mask AND array """
|
||||
self.write_mask_and_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def compute_instance_offsets(self):
|
||||
"""
|
||||
Compute the empty instance offsets for port0 and port1 (if needed)
|
||||
|
|
@ -458,19 +442,17 @@ class port_data(design.design):
|
|||
if self.column_mux_offset:
|
||||
self.place_column_mux_array(self.column_mux_offset)
|
||||
|
||||
|
||||
def route_sense_amp_out(self, port):
|
||||
""" Add pins for the sense amp output """
|
||||
|
||||
for bit in range(self.word_size):
|
||||
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit))
|
||||
self.add_layout_pin_rect_center(text="dout_{0}".format(bit),
|
||||
layer=data_pin.layer,
|
||||
layer=data_pin.layer,
|
||||
offset=data_pin.center(),
|
||||
height=data_pin.height(),
|
||||
width=data_pin.width())
|
||||
|
||||
|
||||
def route_write_driver_in(self, port):
|
||||
""" Connecting write driver """
|
||||
|
||||
|
|
@ -479,7 +461,6 @@ class port_data(design.design):
|
|||
din_name = "din_{}".format(row)
|
||||
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
|
||||
|
||||
|
||||
def route_write_mask_and_array_in(self, port):
|
||||
""" Add pins for the write mask and array input """
|
||||
|
||||
|
|
@ -488,7 +469,6 @@ class port_data(design.design):
|
|||
bank_wmask_name = "bank_wmask_{}".format(bit)
|
||||
self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name)
|
||||
|
||||
|
||||
def route_write_mask_and_array_to_write_driver(self,port):
|
||||
""" Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write
|
||||
mask AND array output and via for write driver enable """
|
||||
|
|
@ -511,15 +491,15 @@ class port_data(design.design):
|
|||
while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()):
|
||||
loc += 1
|
||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
||||
debug.check(loc<=self.num_wmasks,"Couldn't route the write mask select.")
|
||||
debug.check(loc<=self.num_wmasks,
|
||||
"Couldn't route the write mask select.")
|
||||
else:
|
||||
# Stride by the write size rather than finding the next pin to the right
|
||||
loc += self.write_size
|
||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
||||
|
||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
||||
|
||||
beg_pos = wmask_out_pin.center()
|
||||
middle_pos = vector(length,wmask_out_pin.cy())
|
||||
middle_pos = vector(length, wmask_out_pin.cy())
|
||||
end_pos = vector(length, wdriver_en_pin.cy())
|
||||
|
||||
# Add via for the write driver array's enable input
|
||||
|
|
@ -529,7 +509,6 @@ class port_data(design.design):
|
|||
# Route between write mask AND array and write driver array
|
||||
self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos])
|
||||
|
||||
|
||||
def route_column_mux_to_precharge_array(self, port):
|
||||
""" Routing of BL and BR between col mux and precharge array """
|
||||
|
||||
|
|
@ -547,7 +526,6 @@ class port_data(design.design):
|
|||
num_bits=self.num_cols,
|
||||
inst2_start_bit=insn2_start_bit)
|
||||
|
||||
|
||||
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
|
||||
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||
inst2 = self.sense_amp_array_inst
|
||||
|
|
@ -573,12 +551,9 @@ class port_data(design.design):
|
|||
num_bits=self.word_size,
|
||||
inst1_start_bit=start_bit)
|
||||
|
||||
|
||||
def route_write_driver_to_column_mux_or_precharge_array(self, port):
|
||||
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||
inst2 = self.write_driver_array_inst
|
||||
inst2_bl_name = inst2.mod.get_bl_name()
|
||||
inst2_br_name = inst2.mod.get_br_name()
|
||||
|
||||
if self.col_addr_size>0:
|
||||
# Write driver is connected to the col mux
|
||||
|
|
@ -599,7 +574,6 @@ class port_data(design.design):
|
|||
inst1_bls_template=inst1_bls_templ,
|
||||
inst1_start_bit=start_bit)
|
||||
|
||||
|
||||
def route_write_driver_to_sense_amp(self, port):
|
||||
""" Routing of BL and BR between write driver and sense amp """
|
||||
|
||||
|
|
@ -612,7 +586,6 @@ class port_data(design.design):
|
|||
inst2=inst2,
|
||||
num_bits=self.word_size)
|
||||
|
||||
|
||||
def route_bitline_pins(self):
|
||||
""" Add the bitline pins for the given port """
|
||||
|
||||
|
|
@ -630,12 +603,15 @@ class port_data(design.design):
|
|||
|
||||
for bit in range(self.num_cols):
|
||||
if self.precharge_array_inst:
|
||||
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+bit_offset), "bl_{}".format(bit))
|
||||
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit))
|
||||
self.copy_layout_pin(self.precharge_array_inst,
|
||||
"bl_{}".format(bit + bit_offset),
|
||||
"bl_{}".format(bit))
|
||||
self.copy_layout_pin(self.precharge_array_inst,
|
||||
"br_{}".format(bit + bit_offset),
|
||||
"br_{}".format(bit))
|
||||
else:
|
||||
debug.error("Didn't find precharge array.")
|
||||
|
||||
|
||||
def route_control_pins(self):
|
||||
""" Add the control pins: s_en, p_en_bar, w_en """
|
||||
if self.precharge_array_inst:
|
||||
|
|
@ -656,8 +632,6 @@ class port_data(design.design):
|
|||
if self.write_mask_and_array_inst:
|
||||
self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en")
|
||||
|
||||
|
||||
|
||||
def _group_bitline_instances(self, inst1, inst2, num_bits,
|
||||
inst1_bls_template,
|
||||
inst1_start_bit,
|
||||
|
|
@ -694,17 +668,16 @@ class port_data(design.design):
|
|||
Extracts bl/br pins from an InstanceGroup based on the bit modifier.
|
||||
"""
|
||||
full_bl_name = inst_group.bls_template.format(
|
||||
**{'inst' : inst_group.bl_name,
|
||||
'bit' : inst_group.start_bit + bit}
|
||||
)
|
||||
**{'inst': inst_group.bl_name,
|
||||
'bit': inst_group.start_bit + bit}
|
||||
)
|
||||
full_br_name = inst_group.bls_template.format(
|
||||
**{'inst' : inst_group.br_name,
|
||||
'bit' : inst_group.start_bit + bit}
|
||||
)
|
||||
**{'inst': inst_group.br_name,
|
||||
'bit': inst_group.start_bit + bit}
|
||||
)
|
||||
return (inst_group.inst.get_pin(full_bl_name),
|
||||
inst_group.inst.get_pin(full_br_name))
|
||||
|
||||
|
||||
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bls_template="{inst}_{bit}",
|
||||
inst1_start_bit=0,
|
||||
|
|
@ -721,14 +694,13 @@ class port_data(design.design):
|
|||
|
||||
# Channel route each mux separately since we don't minimize the number
|
||||
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
||||
offset = bot_inst_group.inst.ul() + vector(0,self.m1_pitch)
|
||||
offset = bot_inst_group.inst.ul() + vector(0, self.m1_pitch)
|
||||
for bit in range(num_bits):
|
||||
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
|
||||
top_names = self._get_bitline_pins(top_inst_group, bit)
|
||||
|
||||
route_map = list(zip(bottom_names, top_names))
|
||||
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
|
||||
|
||||
|
||||
def connect_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bls_template="{inst}_{bit}",
|
||||
|
|
@ -742,11 +714,9 @@ class port_data(design.design):
|
|||
"""
|
||||
|
||||
bot_inst_group, top_inst_group = self._group_bitline_instances(
|
||||
inst1, inst2, num_bits,
|
||||
inst1_bls_template, inst1_start_bit,
|
||||
inst2_bls_template, inst2_start_bit)
|
||||
bottom_inst = bot_inst_group.inst
|
||||
top_inst = top_inst_group.inst
|
||||
inst1, inst2, num_bits,
|
||||
inst1_bls_template, inst1_start_bit,
|
||||
inst2_bls_template, inst2_start_bit)
|
||||
|
||||
for col in range(num_bits):
|
||||
bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col)
|
||||
|
|
@ -754,11 +724,15 @@ class port_data(design.design):
|
|||
bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc()
|
||||
top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc()
|
||||
|
||||
yoffset = 0.5*(top_bl.y+bot_bl.y)
|
||||
self.add_path("m2",[bot_bl, vector(bot_bl.x,yoffset),
|
||||
vector(top_bl.x,yoffset), top_bl])
|
||||
self.add_path("m2",[bot_br, vector(bot_br.x,yoffset),
|
||||
vector(top_br.x,yoffset), top_br])
|
||||
yoffset = 0.5 * (top_bl.y + bot_bl.y)
|
||||
self.add_path("m2", [bot_bl,
|
||||
vector(bot_bl.x, yoffset),
|
||||
vector(top_bl.x, yoffset),
|
||||
top_bl])
|
||||
self.add_path("m2", [bot_br,
|
||||
vector(bot_br.x, yoffset),
|
||||
vector(top_br.x, yoffset),
|
||||
top_br])
|
||||
|
||||
def graph_exclude_precharge(self):
|
||||
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from vector import vector
|
|||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class precharge_array(design.design):
|
||||
"""
|
||||
Dynamically generated precharge array of all bitlines. Cols is number
|
||||
|
|
@ -68,11 +69,8 @@ class precharge_array(design.design):
|
|||
size=self.size,
|
||||
bitcell_bl=self.bitcell_bl,
|
||||
bitcell_br=self.bitcell_br)
|
||||
|
||||
|
||||
self.add_mod(self.pc_cell)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
self.add_layout_pin(text="en_bar",
|
||||
|
|
@ -86,20 +84,9 @@ class precharge_array(design.design):
|
|||
|
||||
for i in range(len(self.local_insts)):
|
||||
inst = self.local_insts[i]
|
||||
bl_pin = inst.get_pin("bl")
|
||||
self.add_layout_pin(text="bl_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=bl_pin.ll(),
|
||||
width=drc("minwidth_m2"),
|
||||
height=bl_pin.height())
|
||||
br_pin = inst.get_pin("br")
|
||||
self.add_layout_pin(text="br_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=br_pin.ll(),
|
||||
width=drc("minwidth_m2"),
|
||||
height=bl_pin.height())
|
||||
self.copy_layout_pin(inst, "bl", "bl_{0}".format(i))
|
||||
self.copy_layout_pin(inst, "br", "br_{0}".format(i))
|
||||
|
||||
|
||||
def create_insts(self):
|
||||
"""Creates a precharge array by horizontally tiling the precharge cell"""
|
||||
self.local_insts = []
|
||||
|
|
@ -112,7 +99,6 @@ class precharge_array(design.design):
|
|||
self.local_insts.append(inst)
|
||||
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"])
|
||||
|
||||
|
||||
def place_insts(self):
|
||||
""" Places precharge array by horizontally tiling the precharge cell"""
|
||||
from tech import cell_properties
|
||||
|
|
@ -130,8 +116,11 @@ class precharge_array(design.design):
|
|||
xoffset = xoffset + self.pc_cell.width
|
||||
|
||||
def get_en_cin(self):
|
||||
"""Get the relative capacitance of all the clk connections in the precharge array"""
|
||||
#Assume single port
|
||||
"""
|
||||
Get the relative capacitance of all the clk connections
|
||||
in the precharge array
|
||||
"""
|
||||
# Assume single port
|
||||
precharge_en_cin = self.pc_cell.get_en_cin()
|
||||
return precharge_en_cin*self.columns
|
||||
return precharge_en_cin * self.columns
|
||||
|
||||
|
|
|
|||
|
|
@ -75,8 +75,7 @@ class wordline_driver(design.design):
|
|||
"""
|
||||
|
||||
# Find the x offsets for where the vias/pins should be placed
|
||||
a_xoffset = self.nand_inst[0].rx()
|
||||
b_xoffset = self.inv2_inst[0].lx()
|
||||
a_xoffset = self.nand_inst[0].lx()
|
||||
for num in range(self.rows):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
|
|
@ -90,7 +89,7 @@ class wordline_driver(design.design):
|
|||
supply_pin = self.inv2_inst[num].get_pin(name)
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [a_xoffset, b_xoffset]:
|
||||
for xoffset in [a_xoffset]:
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name, pin_pos)
|
||||
|
||||
|
|
@ -116,7 +115,7 @@ class wordline_driver(design.design):
|
|||
"vdd", "gnd"])
|
||||
|
||||
def place_drivers(self):
|
||||
nand2_xoffset = 2*self.m1_width + 5*self.m1_space
|
||||
nand2_xoffset = 2 * self.m1_width + 5 * self.m1_space
|
||||
inv2_xoffset = nand2_xoffset + self.nand2.width
|
||||
|
||||
self.width = inv2_xoffset + self.inv.width
|
||||
|
|
@ -124,10 +123,10 @@ class wordline_driver(design.design):
|
|||
|
||||
for row in range(self.rows):
|
||||
if (row % 2):
|
||||
y_offset = self.inv.height*(row + 1)
|
||||
y_offset = self.inv.height * (row + 1)
|
||||
inst_mirror = "MX"
|
||||
else:
|
||||
y_offset = self.inv.height*row
|
||||
y_offset = self.inv.height * row
|
||||
inst_mirror = "R0"
|
||||
|
||||
nand2_offset = [nand2_xoffset, y_offset]
|
||||
|
|
@ -169,8 +168,8 @@ class wordline_driver(design.design):
|
|||
zr_pos = nand_inst.get_pin("Z").rc()
|
||||
al_pos = inv2_inst.get_pin("A").lc()
|
||||
# ensure the bend is in the middle
|
||||
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
||||
mid1_pos = vector(0.5 * (zr_pos.x + al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5 * (zr_pos.x + al_pos.x), al_pos.y)
|
||||
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
||||
# connect the decoder input pin to nand2 B
|
||||
|
|
@ -181,7 +180,7 @@ class wordline_driver(design.design):
|
|||
up_or_down = self.m2_space if row % 2 else -self.m2_space
|
||||
input_offset = vector(0, b_pos.y + up_or_down)
|
||||
base_offset = vector(clk_offset.x, input_offset.y)
|
||||
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
|
||||
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
|
||||
mid_via_offset = base_offset + contact_offset
|
||||
|
||||
# must under the clk line in M1
|
||||
|
|
@ -208,7 +207,7 @@ class wordline_driver(design.design):
|
|||
end=wl_offset - vector(self.m1_width, 0))
|
||||
|
||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||
"""
|
||||
"""
|
||||
Follows the clk_buf to a wordline signal adding
|
||||
each stages stage effort to a list.
|
||||
"""
|
||||
|
|
@ -225,7 +224,7 @@ class wordline_driver(design.design):
|
|||
return stage_effort_list
|
||||
|
||||
def get_wl_en_cin(self):
|
||||
"""
|
||||
"""
|
||||
Get the relative capacitance of all
|
||||
the enable connections in the bank
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ class pgate(design.design):
|
|||
if height:
|
||||
self.height = height
|
||||
elif not height:
|
||||
# By default, we make it 8 M1 pitch tall
|
||||
self.height = 8*self.m1_pitch
|
||||
# By default, we make it 10 M1 pitch tall
|
||||
self.height = 10*self.m1_pitch
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
|
|
@ -103,7 +103,7 @@ class pgate(design.design):
|
|||
- vector(0.5 * contact_width - 0.5 * self.poly_width, 0)
|
||||
elif position == "right":
|
||||
contact_offset = left_gate_offset \
|
||||
+ vector(0.5 * contact.width + 0.5 * self.poly_width, 0)
|
||||
+ vector(0.5 * contact_width + 0.5 * self.poly_width, 0)
|
||||
else:
|
||||
debug.error("Invalid contact placement option.", -1)
|
||||
|
||||
|
|
@ -128,16 +128,16 @@ class pgate(design.design):
|
|||
""" Extend the n/p wells to cover whole cell """
|
||||
|
||||
# This should match the cells in the cell library
|
||||
nwell_y_offset = 0.48 * self.height
|
||||
full_height = self.height + 0.5*self.m1_width
|
||||
self.nwell_y_offset = 0.48 * self.height
|
||||
full_height = self.height + 0.5* self.m1_width
|
||||
|
||||
# FIXME: float rounding problem
|
||||
if "nwell" in layer:
|
||||
# Add a rail width to extend the well to the top of the rail
|
||||
nwell_max_offset = max(self.find_highest_layer_coords("nwell").y,
|
||||
full_height)
|
||||
nwell_position = vector(0, nwell_y_offset) - vector(self.well_extend_active, 0)
|
||||
nwell_height = nwell_max_offset - nwell_y_offset
|
||||
nwell_position = vector(0, self.nwell_y_offset) - vector(self.well_extend_active, 0)
|
||||
nwell_height = nwell_max_offset - self.nwell_y_offset
|
||||
self.add_rect(layer="nwell",
|
||||
offset=nwell_position,
|
||||
width=self.well_width,
|
||||
|
|
@ -153,7 +153,7 @@ class pgate(design.design):
|
|||
pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y,
|
||||
-0.5 * self.m1_width)
|
||||
pwell_position = vector(-self.well_extend_active, pwell_min_offset)
|
||||
pwell_height = nwell_y_offset - pwell_position.y
|
||||
pwell_height = self.nwell_y_offset - pwell_position.y
|
||||
self.add_rect(layer="pwell",
|
||||
offset=pwell_position,
|
||||
width=self.well_width,
|
||||
|
|
@ -264,3 +264,12 @@ class pgate(design.design):
|
|||
# offset=implant_offset,
|
||||
# width=implant_width,
|
||||
# height=implant_height)
|
||||
|
||||
def determine_width(self):
|
||||
""" Determine the width based on the well contacts (assumed to be on the right side) """
|
||||
# Width is determined by well contact and spacing and allowing a supply via between each cell
|
||||
self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ class pinv(pgate.pgate):
|
|||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
self.setup_layout_constants()
|
||||
self.place_ptx()
|
||||
self.add_well_contacts()
|
||||
self.determine_width()
|
||||
self.extend_wells()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
|
|
@ -147,21 +147,6 @@ class pinv(pgate.pgate):
|
|||
debug.check(self.pmos_width >= drc("minwidth_tx"),
|
||||
"Cannot finger PMOS transistors to fit cell height.")
|
||||
|
||||
def setup_layout_constants(self):
|
||||
"""
|
||||
Compute the width and height
|
||||
"""
|
||||
|
||||
# the width is determined the multi-finger PMOS device width plus
|
||||
# the well contact width, spacing between them
|
||||
# space is for power supply contact to nwell m1 spacing
|
||||
self.width = self.pmos.active_offset.x + self.pmos.active_width \
|
||||
+ self.active_space + contact.nwell_contact.width \
|
||||
+ 0.5 * self.nwell_enclose_active \
|
||||
+ self.m1_space
|
||||
# This includes full enclosures on each end
|
||||
self.well_width = self.width + 2*self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
def add_ptx(self):
|
||||
""" Create the PMOS and NMOS transistors. """
|
||||
|
|
|
|||
|
|
@ -49,10 +49,11 @@ class pnand2(pgate.pgate):
|
|||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.setup_layout_constants()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.determine_width()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
|
@ -96,15 +97,6 @@ class pnand2(pgate.pgate):
|
|||
# source and drain pins
|
||||
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
||||
|
||||
# Two PMOS devices and a well contact. Separation between each.
|
||||
# Enclosure space on the sides.
|
||||
self.width = 2 * self.pmos.active_width + contact.active_contact.width \
|
||||
+ 2 * self.active_space \
|
||||
+ 0.5 * self.nwell_enclose_active
|
||||
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
# This is the extra space needed to ensure DRC rules
|
||||
# to the active contacts
|
||||
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
|
||||
|
|
@ -190,16 +182,16 @@ class pnand2(pgate.pgate):
|
|||
|
||||
def route_inputs(self):
|
||||
""" Route the A and B inputs """
|
||||
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
|
||||
+ self.m2_space + 0.5 * self.m2_width
|
||||
inputB_yoffset = self.nmos2_inst.uy() + 0.5 * contact.poly_contact.height
|
||||
self.route_input_gate(self.pmos2_inst,
|
||||
self.nmos2_inst,
|
||||
inputB_yoffset,
|
||||
"B",
|
||||
position="center")
|
||||
position="right")
|
||||
|
||||
# This will help with the wells and the input/output placement
|
||||
self.inputA_yoffset = inputB_yoffset + self.input_spacing
|
||||
self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \
|
||||
- contact.poly_contact.height
|
||||
self.route_input_gate(self.pmos1_inst,
|
||||
self.nmos1_inst,
|
||||
self.inputA_yoffset,
|
||||
|
|
@ -242,8 +234,8 @@ class pnand2(pgate.pgate):
|
|||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer="m1",
|
||||
offset=out_offset,
|
||||
width=contact.m1_via.first_layer_height,
|
||||
height=contact.m1_via.first_layer_width)
|
||||
width=contact.m1_via.first_layer_width,
|
||||
height=contact.m1_via.first_layer_height)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Returns dynamic and leakage power. Results in nW"""
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ class pnand3(pgate.pgate):
|
|||
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||
|
||||
# FIXME: Allow these to be sized
|
||||
debug.check(size == 1,"Size 1 pnand3 is only supported now.")
|
||||
debug.check(size == 1,
|
||||
"Size 1 pnand3 is only supported now.")
|
||||
self.tx_mults = 1
|
||||
|
||||
# Creates the netlist and layout
|
||||
|
|
@ -57,14 +58,15 @@ class pnand3(pgate.pgate):
|
|||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.setup_layout_constants()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.determine_width()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
||||
|
||||
def add_ptx(self):
|
||||
""" Create the PMOS and NMOS transistors. """
|
||||
self.nmos = factory.create(module_type="ptx",
|
||||
|
|
@ -87,16 +89,9 @@ class pnand3(pgate.pgate):
|
|||
""" Pre-compute some handy layout parameters. """
|
||||
|
||||
# Compute the overlap of the source and drain pins
|
||||
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
||||
|
||||
# Two PMOS devices and a well contact. Separation between each.
|
||||
# Enclosure space on the sides.
|
||||
self.width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
|
||||
+ 2 * self.active_space + 0.5 * self.nwell_enclose_active \
|
||||
- self.overlap_offset.x
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
overlap_xoffset = self.pmos.get_pin("D").ll().x - self.pmos.get_pin("S").ll().x
|
||||
self.ptx_offset = vector(overlap_xoffset, 0)
|
||||
|
||||
# This is the extra space needed to ensure DRC rules
|
||||
# to the active contacts
|
||||
nmos = factory.create(module_type="ptx", tx_type="nmos")
|
||||
|
|
@ -153,30 +148,25 @@ class pnand3(pgate.pgate):
|
|||
"""
|
||||
|
||||
pmos1_pos = vector(self.pmos.active_offset.x,
|
||||
self.height - self.pmos.active_height \
|
||||
- self.top_bottom_space)
|
||||
self.height - self.pmos.active_height - self.top_bottom_space)
|
||||
self.pmos1_inst.place(pmos1_pos)
|
||||
|
||||
pmos2_pos = pmos1_pos + self.overlap_offset
|
||||
pmos2_pos = pmos1_pos + self.ptx_offset
|
||||
self.pmos2_inst.place(pmos2_pos)
|
||||
|
||||
self.pmos3_pos = pmos2_pos + self.overlap_offset
|
||||
self.pmos3_pos = pmos2_pos + self.ptx_offset
|
||||
self.pmos3_inst.place(self.pmos3_pos)
|
||||
|
||||
|
||||
nmos1_pos = vector(self.pmos.active_offset.x,
|
||||
self.top_bottom_space)
|
||||
self.nmos1_inst.place(nmos1_pos)
|
||||
|
||||
nmos2_pos = nmos1_pos + self.overlap_offset
|
||||
nmos2_pos = nmos1_pos + self.ptx_offset
|
||||
self.nmos2_inst.place(nmos2_pos)
|
||||
|
||||
self.nmos3_pos = nmos2_pos + self.overlap_offset
|
||||
self.nmos3_pos = nmos2_pos + self.ptx_offset
|
||||
self.nmos3_inst.place(self.nmos3_pos)
|
||||
|
||||
# This will help with the wells and the input/output placement
|
||||
self.output_pos = vector(0, 0.5*self.height)
|
||||
|
||||
def add_well_contacts(self):
|
||||
""" Add n/p well taps to the layout and connect to supplies """
|
||||
|
||||
|
|
@ -188,40 +178,36 @@ class pnand3(pgate.pgate):
|
|||
|
||||
self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
|
||||
|
||||
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
|
||||
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
|
||||
|
||||
self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd")
|
||||
|
||||
def route_inputs(self):
|
||||
""" Route the A and B inputs """
|
||||
# wire space or wire and one contact space
|
||||
metal_spacing = max(self.m1_space + self.m1_width,
|
||||
self.m2_space + self.m2_width,
|
||||
self.m1_space + 0.5 *contact.poly_contact.width + 0.5 * self.m1_width)
|
||||
""" Route the A and B and C inputs """
|
||||
|
||||
active_spacing = max(self.m1_space,
|
||||
0.5 * contact.poly_contact.first_layer_width + self.poly_to_active)
|
||||
inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing
|
||||
m1_pitch = self.m1_space + contact.m1_via.first_layer_height
|
||||
# Put B right on the well line
|
||||
self.inputB_yoffset = self.nwell_y_offset
|
||||
self.route_input_gate(self.pmos2_inst,
|
||||
self.nmos2_inst,
|
||||
self.inputB_yoffset,
|
||||
"B",
|
||||
position="center")
|
||||
|
||||
self.inputC_yoffset = self.inputB_yoffset - m1_pitch
|
||||
self.route_input_gate(self.pmos3_inst,
|
||||
self.nmos3_inst,
|
||||
inputC_yoffset,
|
||||
self.inputC_yoffset,
|
||||
"C",
|
||||
position="center")
|
||||
|
||||
inputB_yoffset = inputC_yoffset + metal_spacing
|
||||
self.route_input_gate(self.pmos2_inst,
|
||||
self.nmos2_inst,
|
||||
inputB_yoffset,
|
||||
"B",
|
||||
position="center")
|
||||
|
||||
self.inputA_yoffset = inputB_yoffset + metal_spacing
|
||||
self.inputA_yoffset = self.inputB_yoffset + m1_pitch
|
||||
self.route_input_gate(self.pmos1_inst,
|
||||
self.nmos1_inst,
|
||||
self.inputA_yoffset,
|
||||
"A",
|
||||
position="center")
|
||||
|
||||
|
||||
def route_output(self):
|
||||
""" Route the Z output """
|
||||
# PMOS1 drain
|
||||
|
|
@ -243,7 +229,7 @@ class pnand3(pgate.pgate):
|
|||
directions=("V", "V"))
|
||||
|
||||
# PMOS3 and NMOS3 are drain aligned
|
||||
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.uc()])
|
||||
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.center()])
|
||||
# Route in the A input track (top track)
|
||||
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
|
||||
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
|
||||
|
|
@ -273,7 +259,7 @@ class pnand3(pgate.pgate):
|
|||
# In fF
|
||||
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||
transition_prob = 0.1094
|
||||
return transition_prob *(c_load + c_para)
|
||||
return transition_prob * (c_load + c_para)
|
||||
|
||||
def input_load(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
|
|
|
|||
|
|
@ -48,10 +48,11 @@ class pnor2(pgate.pgate):
|
|||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.setup_layout_constants()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.determine_width()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from tech import parameter
|
|||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
from tech import drc
|
||||
|
||||
|
||||
class precharge(design.design):
|
||||
|
|
@ -30,7 +31,16 @@ class precharge(design.design):
|
|||
self.width = self.bitcell.width
|
||||
self.bitcell_bl = bitcell_bl
|
||||
self.bitcell_br = bitcell_br
|
||||
|
||||
self.bitcell_bl_pin =self.bitcell.get_pin(self.bitcell_bl)
|
||||
self.bitcell_br_pin =self.bitcell.get_pin(self.bitcell_br)
|
||||
|
||||
if self.bitcell_bl_pin.layer == "m1":
|
||||
self.bitline_layer = "m1"
|
||||
self.en_layer = "m2"
|
||||
else:
|
||||
self.bitline_layer = "m2"
|
||||
self.en_layer = "m1"
|
||||
|
||||
# Creates the netlist and layout
|
||||
# Since it has variable height, it is not a pgate.
|
||||
self.create_netlist()
|
||||
|
|
@ -50,6 +60,7 @@ class precharge(design.design):
|
|||
self.create_ptx()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place_ptx()
|
||||
self.connect_poly()
|
||||
self.route_en()
|
||||
|
|
@ -78,18 +89,24 @@ class precharge(design.design):
|
|||
|
||||
# Adds the rail across the width of the cell
|
||||
vdd_position = vector(0.5 * self.width, self.height)
|
||||
self.add_rect_center(layer="m1",
|
||||
layer_width = drc("minwidth_" + self.en_layer)
|
||||
self.add_rect_center(layer=self.en_layer,
|
||||
offset=vdd_position,
|
||||
width=self.width,
|
||||
height=self.m1_width)
|
||||
height=layer_width)
|
||||
|
||||
pmos_pin = self.upper_pmos2_inst.get_pin("S")
|
||||
# center of vdd rail
|
||||
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
|
||||
self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos])
|
||||
if self.en_layer != "m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pmos_vdd_pos)
|
||||
|
||||
|
||||
|
||||
# Add vdd pin above the transistor
|
||||
self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
|
||||
self.add_power_pin("vdd", self.well_contact_pos, vertical=True)
|
||||
|
||||
def create_ptx(self):
|
||||
"""
|
||||
|
|
@ -113,6 +130,8 @@ class precharge(design.design):
|
|||
Place both the upper_pmos and lower_pmos to the module
|
||||
"""
|
||||
|
||||
# reserve some offset to jog the bitlines
|
||||
self.initial_yoffset = self.pmos.active_offset.y + self.m2_pitch
|
||||
# Compute the other pmos2 location,
|
||||
# but determining offset to overlap the source and drain pins
|
||||
overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
||||
|
|
@ -120,17 +139,18 @@ class precharge(design.design):
|
|||
contact_xdiff = self.pmos.get_pin("S").lx()
|
||||
|
||||
# adds the lower pmos to layout
|
||||
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
|
||||
bl_xoffset = self.bitcell_bl_pin.lx()
|
||||
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
|
||||
self.nwell_enclose_active),
|
||||
self.pmos.active_offset.y)
|
||||
self.initial_yoffset)
|
||||
self.lower_pmos_inst.place(self.lower_pmos_position)
|
||||
|
||||
# adds the upper pmos(s) to layout
|
||||
ydiff = self.pmos.height + 2 * self.m1_space + contact.poly_contact.width
|
||||
# adds the upper pmos(s) to layout with 2 M2 tracks
|
||||
ydiff = self.pmos.height + self.m2_pitch
|
||||
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
|
||||
self.upper_pmos1_inst.place(self.upper_pmos1_pos)
|
||||
|
||||
# Second pmos to the right of the first
|
||||
upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset
|
||||
self.upper_pmos2_inst.place(upper_pmos2_pos)
|
||||
|
||||
|
|
@ -161,16 +181,21 @@ class precharge(design.design):
|
|||
"""
|
||||
Adds the en input rail, en contact/vias, and connects to the pmos
|
||||
"""
|
||||
|
||||
# adds the en contact to connect the gates to the en rail on metal1
|
||||
|
||||
# adds the en contact to connect the gates to the en rail
|
||||
# midway in the 4 M2 tracks
|
||||
offset = self.lower_pmos_inst.get_pin("G").ul() \
|
||||
+ vector(0, 0.5 * self.poly_space)
|
||||
+ vector(0, 0.5 * self.m2_pitch)
|
||||
self.add_via_center(layers=self.poly_stack,
|
||||
offset=offset)
|
||||
if self.en_layer == "m2":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=offset)
|
||||
|
||||
|
||||
# adds the en rail on metal1
|
||||
self.add_layout_pin_segment_center(text="en_bar",
|
||||
layer="m1",
|
||||
layer=self.en_layer,
|
||||
start=offset.scale(0, 1),
|
||||
end=offset.scale(0, 1) + vector(self.width, 0))
|
||||
|
||||
|
|
@ -180,16 +205,15 @@ class precharge(design.design):
|
|||
"""
|
||||
|
||||
# adds the contact from active to metal1
|
||||
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
|
||||
+ vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \
|
||||
+ self.nwell_extend_active)
|
||||
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
|
||||
+ vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \
|
||||
+ self.nwell_extend_active)
|
||||
self.add_via_center(layers=self.active_stack,
|
||||
offset=well_contact_pos,
|
||||
offset=self.well_contact_pos,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
|
||||
# leave an extra pitch for the height
|
||||
self.height = well_contact_pos.y + contact.active_contact.height + self.m1_pitch
|
||||
self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space
|
||||
|
||||
# nwell should span the whole design since it is pmos only
|
||||
self.add_rect(layer="nwell",
|
||||
|
|
@ -201,80 +225,86 @@ class precharge(design.design):
|
|||
"""
|
||||
Adds both bit-line and bit-line-bar to the module
|
||||
"""
|
||||
|
||||
# adds the BL on metal 2
|
||||
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \
|
||||
- vector(0.5 * self.m2_width, 0)
|
||||
self.bl_pin = self.add_layout_pin(text="bl",
|
||||
layer="m2",
|
||||
offset=offset,
|
||||
height=self.height)
|
||||
layer_width = drc("minwidth_" + self.bitline_layer)
|
||||
layer_space = drc("{0}_to_{0}".format(self.bitline_layer))
|
||||
|
||||
# adds the BR on metal 2
|
||||
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \
|
||||
- vector(0.5 * self.m2_width, 0)
|
||||
self.br_pin = self.add_layout_pin(text="br",
|
||||
layer="m2",
|
||||
offset=offset,
|
||||
height=self.height)
|
||||
# adds the BL
|
||||
self.bl_xoffset = layer_space + 0.5 * layer_width
|
||||
top_pos = vector(self.bl_xoffset, self.height)
|
||||
pin_pos = vector(self.bl_xoffset, 0)
|
||||
self.add_path(self.bitline_layer, [top_pos, pin_pos])
|
||||
self.bl_pin = self.add_layout_pin_segment_center(text="bl",
|
||||
layer=self.bitline_layer,
|
||||
start=pin_pos,
|
||||
end=top_pos)
|
||||
|
||||
# adds the BR
|
||||
self.br_xoffset = self.width - layer_space - 0.5 * layer_width
|
||||
top_pos = vector(self.br_xoffset, self.height)
|
||||
pin_pos = vector(self.br_xoffset, 0)
|
||||
self.add_path(self.bitline_layer, [top_pos, pin_pos])
|
||||
self.br_pin = self.add_layout_pin_segment_center(text="br",
|
||||
layer=self.bitline_layer,
|
||||
start=pin_pos,
|
||||
end=top_pos)
|
||||
|
||||
def connect_to_bitlines(self):
|
||||
"""
|
||||
Connect the bitlines to the devices
|
||||
"""
|
||||
self.add_bitline_contacts()
|
||||
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),
|
||||
self.get_pin("bl"))
|
||||
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),
|
||||
self.get_pin("bl"))
|
||||
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),
|
||||
self.get_pin("br"))
|
||||
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),
|
||||
self.get_pin("br"))
|
||||
self.connect_pmos(self.lower_pmos_inst.get_pin("S"),
|
||||
self.bl_xoffset)
|
||||
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),
|
||||
self.br_xoffset)
|
||||
|
||||
self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),
|
||||
self.bl_xoffset)
|
||||
self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),
|
||||
self.br_xoffset)
|
||||
|
||||
def add_bitline_contacts(self):
|
||||
"""
|
||||
Adds contacts/via from metal1 to metal2 for bit-lines
|
||||
"""
|
||||
|
||||
upper_pin = self.upper_pmos1_inst.get_pin("S")
|
||||
lower_pin = self.lower_pmos_inst.get_pin("S")
|
||||
# No contacts needed if M1
|
||||
if self.bitline_layer == "m1":
|
||||
return
|
||||
|
||||
# BL goes up to M2 at the transistor
|
||||
self.bl_contact =self.add_via_center(layers=self.m1_stack,
|
||||
offset=upper_pin.center(),
|
||||
# BL
|
||||
lower_pin = self.lower_pmos_inst.get_pin("S")
|
||||
self.lower_via = self.add_via_center(layers=self.m1_stack,
|
||||
offset=lower_pin.center(),
|
||||
directions=("V", "V"))
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=lower_pin.center(),
|
||||
directions=("V", "V"))
|
||||
|
||||
# BR routes over on M1 first
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=vector(self.br_pin.cx(), upper_pin.cy()),
|
||||
directions=("V", "V"))
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=vector(self.br_pin.cx(), lower_pin.cy()),
|
||||
directions=("V", "V"))
|
||||
lower_pin = self.lower_pmos_inst.get_pin("D")
|
||||
self.lower_via = self.add_via_center(layers=self.m1_stack,
|
||||
offset=lower_pin.center(),
|
||||
directions=("V", "V"))
|
||||
|
||||
# BR
|
||||
upper_pin = self.upper_pmos1_inst.get_pin("S")
|
||||
self.upper_via2 = self.add_via_center(layers=self.m1_stack,
|
||||
offset=upper_pin.center(),
|
||||
directions=("V", "V"))
|
||||
|
||||
def connect_pmos_m1(self, pmos_pin, bit_pin):
|
||||
upper_pin = self.upper_pmos2_inst.get_pin("D")
|
||||
self.upper_via2 = self.add_via_center(layers=self.m1_stack,
|
||||
offset=upper_pin.center(),
|
||||
directions=("V", "V"))
|
||||
|
||||
def connect_pmos(self, pmos_pin, bit_xoffset):
|
||||
"""
|
||||
Connect a pmos pin to bitline pin
|
||||
"""
|
||||
|
||||
left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
|
||||
right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
|
||||
left_pos = vector(min(pmos_pin.cx(), bit_xoffset), pmos_pin.cy())
|
||||
right_pos = vector(max(pmos_pin.cx(), bit_xoffset), pmos_pin.cy())
|
||||
|
||||
self.add_path("m1", [left_pos, right_pos] )
|
||||
|
||||
def connect_pmos_m2(self, pmos_pin, bit_pin):
|
||||
"""
|
||||
Connect a pmos pin to bitline pin
|
||||
"""
|
||||
|
||||
left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
|
||||
right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
|
||||
|
||||
self.add_path("m2", [left_pos, right_pos], self.bl_contact.height)
|
||||
self.add_path(self.bitline_layer,
|
||||
[left_pos, right_pos],
|
||||
width=pmos_pin.height())
|
||||
|
||||
def get_en_cin(self):
|
||||
"""Get the relative capacitance of the enable in the precharge cell"""
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import debug
|
|||
from tech import layer, drc, spice
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
import contact
|
||||
|
||||
|
||||
class ptx(design.design):
|
||||
|
|
@ -28,6 +29,7 @@ class ptx(design.design):
|
|||
tx_type="nmos",
|
||||
connect_active=False,
|
||||
connect_poly=False,
|
||||
series_devices=False,
|
||||
num_contacts=None):
|
||||
# We need to keep unique names because outputting to GDSII
|
||||
# will use the last record with a given name. I.e., you will
|
||||
|
|
@ -50,6 +52,7 @@ class ptx(design.design):
|
|||
self.tx_width = width
|
||||
self.connect_active = connect_active
|
||||
self.connect_poly = connect_poly
|
||||
self.series_devices = series_devices
|
||||
self.num_contacts = num_contacts
|
||||
|
||||
# Since it has variable height, it is not a pgate.
|
||||
|
|
@ -127,27 +130,25 @@ class ptx(design.design):
|
|||
directions=("V", "V"),
|
||||
dimensions=(1, self.num_contacts))
|
||||
|
||||
# The contacted poly pitch
|
||||
self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width,
|
||||
self.poly_space)
|
||||
|
||||
# The contacted poly pitch
|
||||
self.contact_spacing = 2 * self.contact_to_gate + \
|
||||
self.contact_width + self.poly_width
|
||||
|
||||
# This is measured because of asymmetric enclosure rules
|
||||
active_enclose_contact = 0.5*(self.active_contact.width - self.contact_width)
|
||||
# This is the extra poly spacing due to the poly contact to poly contact pitch
|
||||
# of contacted gates
|
||||
extra_poly_contact_width = contact.poly_contact.width - self.poly_width
|
||||
|
||||
# This is the distance from the side of
|
||||
# poly gate to the contacted end of active
|
||||
# (i.e. the "outside" contacted diffusion sizes)
|
||||
self.end_to_poly = self.active_contact.width - active_enclose_contact + \
|
||||
self.contact_to_gate
|
||||
# This is the spacing between S/D contacts
|
||||
# This is the spacing between the poly gates
|
||||
self.min_poly_pitch = self.poly_space + self.poly_width
|
||||
self.contacted_poly_pitch = self.poly_space + contact.poly_contact.width
|
||||
self.contact_pitch = 2 * self.contact_to_gate + self.poly_width + self.contact_width
|
||||
self.poly_pitch = max(self.min_poly_pitch,
|
||||
self.contacted_poly_pitch,
|
||||
self.contact_pitch)
|
||||
|
||||
self.end_to_contact = 0.5 * self.active_contact.width
|
||||
|
||||
# Active width is determined by enclosure on both ends and contacted pitch,
|
||||
# at least one poly and n-1 poly pitches
|
||||
self.active_width = 2 * self.end_to_poly + self.poly_width + \
|
||||
(self.mults - 1) * self.poly_pitch
|
||||
self.active_width = 2 * self.end_to_contact + self.active_contact.width \
|
||||
+ 2 * self.contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch
|
||||
|
||||
# Active height is just the transistor width
|
||||
self.active_height = self.tx_width
|
||||
|
|
@ -287,13 +288,13 @@ class ptx(design.design):
|
|||
Add the poly gates(s) and (optionally) connect them.
|
||||
"""
|
||||
# poly is one contacted spacing from the end and down an extension
|
||||
poly_offset = self.active_offset \
|
||||
+ vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \
|
||||
+ vector(self.end_to_poly, -self.poly_extend_active)
|
||||
|
||||
poly_offset = self.contact_offset \
|
||||
+ vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.contact_to_gate, 0)
|
||||
|
||||
# poly_positions are the bottom center of the poly gates
|
||||
poly_positions = []
|
||||
|
||||
self.poly_positions = []
|
||||
self.poly_gates = []
|
||||
|
||||
# It is important that these are from left to right,
|
||||
# so that the pins are in the right
|
||||
# order for the accessors
|
||||
|
|
@ -304,16 +305,18 @@ class ptx(design.design):
|
|||
offset=poly_offset,
|
||||
height=self.poly_height,
|
||||
width=self.poly_width)
|
||||
self.add_layout_pin_rect_center(text="G",
|
||||
layer="poly",
|
||||
offset=poly_offset,
|
||||
height=self.poly_height,
|
||||
width=self.poly_width)
|
||||
poly_positions.append(poly_offset)
|
||||
poly_offset = poly_offset + vector(self.poly_pitch,0)
|
||||
gate = self.add_layout_pin_rect_center(text="G",
|
||||
layer="poly",
|
||||
offset=poly_offset,
|
||||
height=self.poly_height,
|
||||
width=self.poly_width)
|
||||
self.poly_positions.append(poly_offset)
|
||||
self.poly_gates.append(gate)
|
||||
|
||||
poly_offset = poly_offset + vector(self.poly_pitch, 0)
|
||||
|
||||
if self.connect_poly:
|
||||
self.connect_fingered_poly(poly_positions)
|
||||
self.connect_fingered_poly(self.poly_positions)
|
||||
|
||||
def add_active(self):
|
||||
"""
|
||||
|
|
@ -362,59 +365,88 @@ class ptx(design.design):
|
|||
"""
|
||||
return 1
|
||||
|
||||
def get_contact_positions(self):
|
||||
"""
|
||||
Create a list of the centers of drain and source contact positions.
|
||||
"""
|
||||
# The first one will always be a source
|
||||
source_positions = [self.contact_offset]
|
||||
drain_positions = []
|
||||
# It is important that these are from left to right,
|
||||
# so that the pins are in the right
|
||||
# order for the accessors.
|
||||
for i in range(self.mults):
|
||||
if i%2:
|
||||
# It's a source... so offset from previous drain.
|
||||
source_positions.append(drain_positions[-1] + vector(self.contact_spacing, 0))
|
||||
else:
|
||||
# It's a drain... so offset from previous source.
|
||||
drain_positions.append(source_positions[-1] + vector(self.contact_spacing, 0))
|
||||
|
||||
return [source_positions,drain_positions]
|
||||
|
||||
def add_active_contacts(self):
|
||||
"""
|
||||
Add the active contacts to the transistor.
|
||||
"""
|
||||
drain_positions = []
|
||||
source_positions = []
|
||||
|
||||
[source_positions,drain_positions] = self.get_contact_positions()
|
||||
|
||||
for pos in source_positions:
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
# Keep a list of the source/drain contacts
|
||||
self.source_contacts = []
|
||||
self.drain_contacts = []
|
||||
|
||||
# First one is always a SOURCE
|
||||
label = "S"
|
||||
pos = self.contact_offset
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text=label,
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text="S",
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
self.source_contacts.append(contact)
|
||||
source_positions.append(pos)
|
||||
|
||||
# Skip these if they are going to be in series
|
||||
if not self.series_devices:
|
||||
for (poly1, poly2) in zip(self.poly_positions, self.poly_positions[1:]):
|
||||
pos = vector(0.5 * (poly1.x + poly2.x),
|
||||
self.contact_offset.y)
|
||||
# Alternate source and drains
|
||||
if label == "S":
|
||||
label = "D"
|
||||
drain_positions.append(pos)
|
||||
else:
|
||||
label = "S"
|
||||
source_positions.append(pos)
|
||||
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text=label,
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
|
||||
for pos in drain_positions:
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
if label == "S":
|
||||
self.source_contacts.append(contact)
|
||||
else:
|
||||
self.drain_contacts.append(contact)
|
||||
|
||||
pos = vector(self.active_offset.x + self.active_width - 0.5 * self.active_contact.width,
|
||||
self.contact_offset.y)
|
||||
# Last one is the opposite of previous
|
||||
if label == "S":
|
||||
label = "D"
|
||||
drain_positions.append(pos)
|
||||
else:
|
||||
label = "S"
|
||||
source_positions.append(pos)
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text=label,
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text="D",
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
if label == "S":
|
||||
self.source_contacts.append(contact)
|
||||
else:
|
||||
self.drain_contacts.append(contact)
|
||||
|
||||
if self.connect_active:
|
||||
self.connect_fingered_active(drain_positions, source_positions)
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
import datetime
|
||||
import getpass
|
||||
import debug
|
||||
from globals import OPTS, print_time
|
||||
from sram_config import sram_config
|
||||
|
||||
|
||||
|
||||
class sram():
|
||||
"""
|
||||
This is not a design module, but contains an SRAM design instance.
|
||||
|
|
@ -28,7 +26,7 @@ class sram():
|
|||
from design import design
|
||||
design.name_map=[]
|
||||
|
||||
debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size,
|
||||
debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size,
|
||||
self.num_words,
|
||||
self.num_banks))
|
||||
start_time = datetime.datetime.now()
|
||||
|
|
@ -40,30 +38,28 @@ class sram():
|
|||
elif self.num_banks == 2:
|
||||
from sram_2bank import sram_2bank as sram
|
||||
else:
|
||||
debug.error("Invalid number of banks.",-1)
|
||||
debug.error("Invalid number of banks.", -1)
|
||||
|
||||
self.s = sram(name, sram_config)
|
||||
self.s = sram(name, sram_config)
|
||||
self.s.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.s.create_layout()
|
||||
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("SRAM creation", datetime.datetime.now(), start_time)
|
||||
|
||||
|
||||
def sp_write(self,name):
|
||||
def sp_write(self, name):
|
||||
self.s.sp_write(name)
|
||||
|
||||
def lef_write(self,name):
|
||||
def lef_write(self, name):
|
||||
self.s.lef_write(name)
|
||||
|
||||
def gds_write(self,name):
|
||||
def gds_write(self, name):
|
||||
self.s.gds_write(name)
|
||||
|
||||
def verilog_write(self,name):
|
||||
def verilog_write(self, name):
|
||||
self.s.verilog_write(name)
|
||||
|
||||
|
||||
def save(self):
|
||||
""" Save all the output files while reporting time to do it as well. """
|
||||
|
||||
|
|
@ -107,7 +103,6 @@ class sram():
|
|||
debug.print_raw("LIB: Characterizing... ")
|
||||
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
|
||||
print_time("Characterization", datetime.datetime.now(), start_time)
|
||||
|
||||
|
||||
# Write the config file
|
||||
start_time = datetime.datetime.now()
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ class sram_1bank(sram_base):
|
|||
sram_base.__init__(self, name, sram_config)
|
||||
|
||||
def create_modules(self):
|
||||
"""
|
||||
"""
|
||||
This adds the modules for a single bank SRAM with control
|
||||
logic.
|
||||
logic.
|
||||
"""
|
||||
|
||||
self.bank_inst=self.create_bank(0)
|
||||
|
|
@ -40,7 +40,7 @@ class sram_1bank(sram_base):
|
|||
self.data_dff_insts = self.create_data_dff()
|
||||
|
||||
def place_instances(self):
|
||||
"""
|
||||
"""
|
||||
This places the instances for a single bank SRAM with control
|
||||
logic and up to 2 ports.
|
||||
"""
|
||||
|
|
@ -53,19 +53,19 @@ class sram_1bank(sram_base):
|
|||
# the sense amps/column mux and cell array)
|
||||
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
||||
# up to the row address DFFs.
|
||||
control_pos = [None]*len(self.all_ports)
|
||||
row_addr_pos = [None]*len(self.all_ports)
|
||||
col_addr_pos = [None]*len(self.all_ports)
|
||||
wmask_pos = [None]*len(self.all_ports)
|
||||
data_pos = [None]*len(self.all_ports)
|
||||
control_pos = [None] * len(self.all_ports)
|
||||
row_addr_pos = [None] * len(self.all_ports)
|
||||
col_addr_pos = [None] * len(self.all_ports)
|
||||
wmask_pos = [None] * len(self.all_ports)
|
||||
data_pos = [None] * len(self.all_ports)
|
||||
|
||||
if self.write_size:
|
||||
max_gap_size = self.m3_pitch*self.word_size + 2*self.m1_pitch
|
||||
max_gap_size_wmask = self.m2_pitch*max(self.num_wmasks+1,self.col_addr_size+1) + 2*self.m1_pitch
|
||||
max_gap_size = self.m3_pitch * self.word_size + 2 * self.m1_pitch
|
||||
max_gap_size_wmask = self.m2_pitch * max(self.num_wmasks + 1, self.col_addr_size + 1) + 2 * self.m1_pitch
|
||||
else:
|
||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||
# The M1 pitch is for supply rail spacings
|
||||
max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch
|
||||
max_gap_size = self.m2_pitch * max(self.word_size + 1,self.col_addr_size + 1) + 2 * self.m1_pitch
|
||||
|
||||
# Port 0
|
||||
port = 0
|
||||
|
|
@ -79,7 +79,7 @@ class sram_1bank(sram_base):
|
|||
|
||||
# Add the data flops below the write mask flops.
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||
-max_gap_size - max_gap_size_wmask - 2*self.dff.height)
|
||||
-max_gap_size - max_gap_size_wmask - 2 * self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port])
|
||||
else:
|
||||
# Add the data flops below the bank to the right of the lower-left of bank array
|
||||
|
|
@ -93,8 +93,7 @@ class sram_1bank(sram_base):
|
|||
self.data_dff_insts[port].place(data_pos[port])
|
||||
else:
|
||||
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,0)
|
||||
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||
|
||||
# Add the col address flops below the bank to the left of the lower-left of bank array
|
||||
if self.col_addr_dff:
|
||||
|
|
@ -106,11 +105,11 @@ class sram_1bank(sram_base):
|
|||
-max_gap_size - self.col_addr_dff_insts[port].height)
|
||||
self.col_addr_dff_insts[port].place(col_addr_pos[port])
|
||||
else:
|
||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x,0)
|
||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||
|
||||
# This includes 2 M2 pitches for the row addr clock line.
|
||||
control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch,
|
||||
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2*self.bank.m2_gap )
|
||||
control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch,
|
||||
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2 * self.bank.m2_gap)
|
||||
self.control_logic_insts[port].place(control_pos[port])
|
||||
|
||||
# The row address bits are placed above the control logic aligned on the right.
|
||||
|
|
@ -133,7 +132,7 @@ class sram_1bank(sram_base):
|
|||
|
||||
# Add the data flops below the write mask flops
|
||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||
self.bank.height + max_gap_size_wmask + max_gap_size + 2*self.dff.height)
|
||||
self.bank.height + max_gap_size_wmask + max_gap_size + 2 * self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||
else:
|
||||
# Add the data flops above the bank to the left of the upper-right of bank array
|
||||
|
|
@ -158,11 +157,10 @@ class sram_1bank(sram_base):
|
|||
col_addr_pos[port] = self.bank_inst.ur()
|
||||
|
||||
# This includes 2 M2 pitches for the row addr clock line
|
||||
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch,
|
||||
self.bank.bank_array_ur.y + self.control_logic_insts[port].height -
|
||||
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch,
|
||||
self.bank.bank_array_ur.y + self.control_logic_insts[port].height - \
|
||||
(self.control_logic_insts[port].height - self.control_logic_insts[port].mod.control_logic_center.y)
|
||||
+ 2*self.bank.m2_gap)
|
||||
#import pdb; pdb.set_trace()
|
||||
+ 2 * self.bank.m2_gap)
|
||||
self.control_logic_insts[port].place(control_pos[port], mirror="XY")
|
||||
|
||||
# The row address bits are placed above the control logic aligned on the left.
|
||||
|
|
@ -172,7 +170,6 @@ class sram_1bank(sram_base):
|
|||
row_addr_pos[port] = vector(x_offset, y_offset)
|
||||
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY")
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
"""
|
||||
Add the top-level pins for a single bank SRAM with control.
|
||||
|
|
@ -180,28 +177,39 @@ class sram_1bank(sram_base):
|
|||
for port in self.all_ports:
|
||||
# Connect the control pins as inputs
|
||||
for signal in self.control_logic_inputs[port] + ["clk"]:
|
||||
self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port))
|
||||
self.copy_layout_pin(self.control_logic_insts[port],
|
||||
signal,
|
||||
signal + "{}".format(port))
|
||||
|
||||
if port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "dout{0}[{1}]".format(port,bit))
|
||||
self.copy_layout_pin(self.bank_inst,
|
||||
"dout{0}_{1}".format(port, bit),
|
||||
"dout{0}[{1}]".format(port, bit))
|
||||
|
||||
# Lower address bits
|
||||
for bit in range(self.col_addr_size):
|
||||
self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit),"addr{0}[{1}]".format(port,bit))
|
||||
self.copy_layout_pin(self.col_addr_dff_insts[port],
|
||||
"din_{}".format(bit),
|
||||
"addr{0}[{1}]".format(port, bit))
|
||||
# Upper address bits
|
||||
for bit in range(self.row_addr_size):
|
||||
self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit),"addr{0}[{1}]".format(port,bit+self.col_addr_size))
|
||||
self.copy_layout_pin(self.row_addr_dff_insts[port],
|
||||
"din_{}".format(bit),
|
||||
"addr{0}[{1}]".format(port, bit + self.col_addr_size))
|
||||
|
||||
if port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "din{0}[{1}]".format(port,bit))
|
||||
self.copy_layout_pin(self.data_dff_insts[port],
|
||||
"din_{}".format(bit),
|
||||
"din{0}[{1}]".format(port, bit))
|
||||
|
||||
if self.write_size:
|
||||
for bit in range(self.num_wmasks):
|
||||
self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port,bit))
|
||||
self.copy_layout_pin(self.wmask_dff_insts[port],
|
||||
"din_{}".format(bit),
|
||||
"wmask{0}[{1}]".format(port, bit))
|
||||
|
||||
|
||||
def route_layout(self):
|
||||
""" Route a single bank SRAM """
|
||||
|
||||
|
|
@ -255,13 +263,15 @@ class sram_1bank(sram_base):
|
|||
offset=clk_steiner_pos)
|
||||
|
||||
# Note, the via to the control logic is taken care of above
|
||||
self.add_wire(("m3","via2","m2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
|
||||
|
||||
if self.col_addr_dff:
|
||||
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
|
||||
dff_clk_pos = dff_clk_pin.center()
|
||||
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
|
||||
self.add_wire(("m3","via2","m2"),[dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
|
||||
if port in self.write_ports:
|
||||
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
|
||||
|
|
@ -269,8 +279,11 @@ class sram_1bank(sram_base):
|
|||
mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y)
|
||||
# In some designs, the steiner via will be too close to the mid_pos via
|
||||
# so make the wire as wide as the contacts
|
||||
self.add_path("m2",[mid_pos, clk_steiner_pos], width=max(m2_via.width,m2_via.height))
|
||||
self.add_wire(("m3","via2","m2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
self.add_path("m2",
|
||||
[mid_pos, clk_steiner_pos],
|
||||
width=max(m2_via.width, m2_via.height))
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
|
||||
if self.write_size:
|
||||
wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk")
|
||||
|
|
@ -280,7 +293,6 @@ class sram_1bank(sram_base):
|
|||
# so make the wire as wide as the contacts
|
||||
self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height))
|
||||
self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
|
||||
|
||||
def route_control_logic(self):
|
||||
""" Route the control logic pins that are not inputs """
|
||||
|
|
@ -291,28 +303,29 @@ class sram_1bank(sram_base):
|
|||
if "clk" in signal:
|
||||
continue
|
||||
src_pin = self.control_logic_insts[port].get_pin(signal)
|
||||
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
|
||||
dest_pin = self.bank_inst.get_pin(signal + "{}".format(port))
|
||||
self.connect_vbus_m2m3(src_pin, dest_pin)
|
||||
|
||||
for port in self.all_ports:
|
||||
# Only input (besides pins) is the replica bitline
|
||||
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
|
||||
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
||||
self.connect_vbus_m2m3(src_pin, dest_pin)
|
||||
|
||||
|
||||
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
||||
|
||||
self.connect_hbus_m2m3(src_pin, dest_pin)
|
||||
|
||||
def route_row_addr_dff(self):
|
||||
""" Connect the output of the row flops to the bank pins """
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.row_addr_size):
|
||||
flop_name = "dout_{}".format(bit)
|
||||
bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size)
|
||||
bank_name = "addr{0}_{1}".format(port, bit + self.col_addr_size)
|
||||
flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name)
|
||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||
flop_pos = flop_pin.center()
|
||||
bank_pos = bank_pin.center()
|
||||
mid_pos = vector(bank_pos.x,flop_pos.y)
|
||||
self.add_wire(("m3","via2","m2"),[flop_pos, mid_pos,bank_pos])
|
||||
mid_pos = vector(bank_pos.x, flop_pos.y)
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[flop_pos, mid_pos, bank_pos])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=flop_pos)
|
||||
|
||||
|
|
@ -320,11 +333,11 @@ class sram_1bank(sram_base):
|
|||
""" Connect the output of the col flops to the bank pins """
|
||||
for port in self.all_ports:
|
||||
if port%2:
|
||||
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size+2)*self.m1_pitch)
|
||||
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size + 2) * self.m1_pitch)
|
||||
else:
|
||||
offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||
offset = self.col_addr_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
||||
|
||||
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
|
||||
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
|
||||
col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
|
||||
pitch=self.m1_pitch,
|
||||
offset=offset,
|
||||
|
|
@ -335,10 +348,9 @@ class sram_1bank(sram_base):
|
|||
data_dff_map = zip(dff_names, bus_names)
|
||||
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets)
|
||||
|
||||
bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)]
|
||||
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
|
||||
data_bank_map = zip(bank_names, bus_names)
|
||||
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets)
|
||||
|
||||
|
||||
def route_data_dff(self):
|
||||
""" Connect the output of the data flops to the write driver """
|
||||
|
|
@ -346,14 +358,14 @@ class sram_1bank(sram_base):
|
|||
for port in self.write_ports:
|
||||
if self.write_size:
|
||||
if port % 2:
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2)*self.m3_pitch)
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m3_pitch)
|
||||
else:
|
||||
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch)
|
||||
else:
|
||||
if port%2:
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch)
|
||||
if port % 2:
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m1_pitch)
|
||||
else:
|
||||
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
||||
|
||||
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
||||
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
|
||||
|
|
@ -362,13 +374,13 @@ class sram_1bank(sram_base):
|
|||
pin_offset = self.data_dff_insts[port].get_pin(x).center()
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pin_offset,
|
||||
directions = ("V", "V"))
|
||||
directions=("V", "V"))
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=pin_offset)
|
||||
self.add_via_center(layers=self.m3_stack,
|
||||
offset=pin_offset)
|
||||
|
||||
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
|
||||
bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size)]
|
||||
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
if self.write_size:
|
||||
for x in bank_names:
|
||||
|
|
@ -397,7 +409,7 @@ class sram_1bank(sram_base):
|
|||
# This is where the channel will start (y-dimension at least)
|
||||
for port in self.write_ports:
|
||||
if port % 2:
|
||||
offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks+2) * self.m1_pitch)
|
||||
offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks + 2) * self.m1_pitch)
|
||||
else:
|
||||
offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
||||
|
||||
|
|
@ -416,16 +428,14 @@ class sram_1bank(sram_base):
|
|||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=offset_pin)
|
||||
|
||||
|
||||
route_map = list(zip(bank_pins, dff_pins))
|
||||
self.create_horizontal_channel_route(netlist=route_map,
|
||||
offset=offset,
|
||||
layer_stack=self.m1_stack)
|
||||
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
|
|
@ -438,7 +448,7 @@ class sram_1bank(sram_base):
|
|||
|
||||
def graph_exclude_data_dff(self):
|
||||
"""Removes data dff and wmask dff (if applicable) from search graph. """
|
||||
#Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay.
|
||||
# Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay.
|
||||
for inst in self.data_dff_insts:
|
||||
self.graph_inst_exclude.add(inst)
|
||||
if self.write_size:
|
||||
|
|
@ -447,7 +457,7 @@ class sram_1bank(sram_base):
|
|||
|
||||
def graph_exclude_addr_dff(self):
|
||||
"""Removes data dff from search graph. """
|
||||
#Address is considered not part of the critical path, subjectively removed
|
||||
# Address is considered not part of the critical path, subjectively removed
|
||||
for inst in self.row_addr_dff_insts:
|
||||
self.graph_inst_exclude.add(inst)
|
||||
|
||||
|
|
@ -457,27 +467,27 @@ class sram_1bank(sram_base):
|
|||
|
||||
def graph_exclude_ctrl_dffs(self):
|
||||
"""Exclude dffs for CSB, WEB, etc from graph"""
|
||||
#Insts located in control logic, exclusion function called here
|
||||
# Insts located in control logic, exclusion function called here
|
||||
for inst in self.control_logic_insts:
|
||||
inst.mod.graph_exclude_dffs()
|
||||
|
||||
def get_sen_name(self, sram_name, port=0):
|
||||
"""Returns the s_en spice name."""
|
||||
#Naming scheme is hardcoded using this function, should be built into the
|
||||
#graph in someway.
|
||||
# Naming scheme is hardcoded using this function, should be built into the
|
||||
# graph in someway.
|
||||
sen_name = "s_en{}".format(port)
|
||||
control_conns = self.get_conns(self.control_logic_insts[port])
|
||||
#Sanity checks
|
||||
# Sanity checks
|
||||
if sen_name not in control_conns:
|
||||
debug.error("Signal={} not contained in control logic connections={}"\
|
||||
.format(sen_name, control_conns))
|
||||
debug.error("Signal={} not contained in control logic connections={}".format(sen_name,
|
||||
control_conns))
|
||||
if sen_name in self.pins:
|
||||
debug.error("Internal signal={} contained in port list. Name defined by the parent.")
|
||||
debug.error("Internal signal={} contained in port list. Name defined by the parent.".format(sen_name))
|
||||
return "X{}.{}".format(sram_name, sen_name)
|
||||
|
||||
def get_cell_name(self, inst_name, row, col):
|
||||
"""Gets the spice name of the target bitcell."""
|
||||
#Sanity check in case it was forgotten
|
||||
# Sanity check in case it was forgotten
|
||||
if inst_name.find('x') != 0:
|
||||
inst_name = 'x'+inst_name
|
||||
return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col)
|
||||
inst_name = 'x' + inst_name
|
||||
return self.bank_inst.mod.get_cell_name(inst_name + '.x' + self.bank_inst.name, row, col)
|
||||
|
|
|
|||
|
|
@ -5,20 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
import datetime
|
||||
import getpass
|
||||
import debug
|
||||
from datetime import datetime
|
||||
from importlib import reload
|
||||
from vector import vector
|
||||
from globals import OPTS, print_time
|
||||
import logical_effort
|
||||
from design import design
|
||||
from verilog import verilog
|
||||
from lef import lef
|
||||
from sram_factory import factory
|
||||
import logical_effort
|
||||
|
||||
|
||||
class sram_base(design, verilog, lef):
|
||||
"""
|
||||
|
|
@ -36,11 +32,11 @@ class sram_base(design, verilog, lef):
|
|||
self.bank_insts = []
|
||||
|
||||
if self.write_size:
|
||||
self.num_wmasks = int(self.word_size/self.write_size)
|
||||
self.num_wmasks = int(self.word_size / self.write_size)
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
#For logical effort delay calculations.
|
||||
# For logical effort delay calculations.
|
||||
self.all_mods_except_control_done = False
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -48,11 +44,11 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("din{0}[{1}]".format(port,bit),"INPUT")
|
||||
self.add_pin("din{0}[{1}]".format(port, bit), "INPUT")
|
||||
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.addr_size):
|
||||
self.add_pin("addr{0}[{1}]".format(port,bit),"INPUT")
|
||||
self.add_pin("addr{0}[{1}]".format(port, bit), "INPUT")
|
||||
|
||||
# These are used to create the physical pins
|
||||
self.control_logic_inputs = []
|
||||
|
|
@ -69,27 +65,26 @@ class sram_base(design, verilog, lef):
|
|||
self.control_logic_outputs.append(self.control_logic_r.get_outputs())
|
||||
|
||||
for port in self.all_ports:
|
||||
self.add_pin("csb{}".format(port),"INPUT")
|
||||
self.add_pin("csb{}".format(port), "INPUT")
|
||||
for port in self.readwrite_ports:
|
||||
self.add_pin("web{}".format(port),"INPUT")
|
||||
self.add_pin("web{}".format(port), "INPUT")
|
||||
for port in self.all_ports:
|
||||
self.add_pin("clk{}".format(port),"INPUT")
|
||||
self.add_pin("clk{}".format(port), "INPUT")
|
||||
# add the optional write mask pins
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.num_wmasks):
|
||||
self.add_pin("wmask{0}[{1}]".format(port,bit),"INPUT")
|
||||
self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT")
|
||||
for port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("dout{0}[{1}]".format(port,bit),"OUTPUT")
|
||||
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
|
||||
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def create_netlist(self):
|
||||
""" Netlist creation """
|
||||
|
||||
start_time = datetime.now()
|
||||
start_time = datetime.datetime.now()
|
||||
|
||||
# Must create the control logic before pins to get the pins
|
||||
self.add_modules()
|
||||
|
|
@ -100,23 +95,21 @@ class sram_base(design, verilog, lef):
|
|||
self.width=0
|
||||
self.height=0
|
||||
|
||||
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Submodules",datetime.now(), start_time)
|
||||
|
||||
print_time("Submodules", datetime.datetime.now(), start_time)
|
||||
|
||||
def create_layout(self):
|
||||
""" Layout creation """
|
||||
start_time = datetime.now()
|
||||
""" Layout creation """
|
||||
start_time = datetime.datetime.now()
|
||||
self.place_instances()
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Placement",datetime.now(), start_time)
|
||||
print_time("Placement", datetime.datetime.now(), start_time)
|
||||
|
||||
start_time = datetime.now()
|
||||
start_time = datetime.datetime.now()
|
||||
self.route_layout()
|
||||
self.route_supplies()
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Routing",datetime.now(), start_time)
|
||||
print_time("Routing", datetime.datetime.now(), start_time)
|
||||
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
|
|
@ -126,14 +119,14 @@ class sram_base(design, verilog, lef):
|
|||
self.width = highest_coord[0]
|
||||
self.height = highest_coord[1]
|
||||
|
||||
start_time = datetime.now()
|
||||
start_time = datetime.datetime.now()
|
||||
# We only enable final verification if we have routed the design
|
||||
self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True)
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Verification",datetime.now(), start_time)
|
||||
print_time("Verification", datetime.datetime.now(), start_time)
|
||||
|
||||
def create_modules(self):
|
||||
debug.error("Must override pure virtual function.",-1)
|
||||
debug.error("Must override pure virtual function.", -1)
|
||||
|
||||
def route_supplies(self):
|
||||
""" Route the supply grid and connect the pins to them. """
|
||||
|
|
@ -141,8 +134,8 @@ class sram_base(design, verilog, lef):
|
|||
# Copy the pins to the top level
|
||||
# This will either be used to route or left unconnected.
|
||||
for inst in self.insts:
|
||||
self.copy_power_pins(inst,"vdd")
|
||||
self.copy_power_pins(inst,"gnd")
|
||||
self.copy_power_pins(inst, "vdd")
|
||||
self.copy_power_pins(inst, "gnd")
|
||||
|
||||
import tech
|
||||
if not OPTS.route_supplies:
|
||||
|
|
@ -164,33 +157,31 @@ class sram_base(design, verilog, lef):
|
|||
from supply_grid_router import supply_grid_router as router
|
||||
rtr=router(grid_stack, self)
|
||||
rtr.route()
|
||||
|
||||
|
||||
def compute_bus_sizes(self):
|
||||
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
||||
|
||||
# address size + control signals + one-hot bank select signals
|
||||
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks,2) + 1
|
||||
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks, 2) + 1
|
||||
# data bus size
|
||||
self.num_horizontal_line = self.word_size
|
||||
|
||||
self.vertical_bus_width = self.m2_pitch*self.num_vertical_line
|
||||
self.vertical_bus_width = self.m2_pitch * self.num_vertical_line
|
||||
# vertical bus height depends on 2 or 4 banks
|
||||
|
||||
self.data_bus_height = self.m3_pitch*self.num_horizontal_line
|
||||
self.data_bus_width = 2*(self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
|
||||
self.data_bus_height = self.m3_pitch * self.num_horizontal_line
|
||||
self.data_bus_width = 2 * (self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
|
||||
|
||||
self.control_bus_height = self.m1_pitch*(self.control_size+2)
|
||||
self.control_bus_height = self.m1_pitch * (self.control_size + 2)
|
||||
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
|
||||
|
||||
self.supply_bus_height = self.m1_pitch*2 # 2 for vdd/gnd placed with control bus
|
||||
self.supply_bus_height = self.m1_pitch * 2 # 2 for vdd/gnd placed with control bus
|
||||
self.supply_bus_width = self.data_bus_width
|
||||
|
||||
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
|
||||
debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width,
|
||||
debug.check(self.bank.width + self.vertical_bus_width > 0.9 * self.control_logic.width,
|
||||
"Bank is too small compared to control logic.")
|
||||
|
||||
|
||||
def add_busses(self):
|
||||
""" Add the horizontal and vertical busses """
|
||||
# Vertical bus
|
||||
|
|
@ -213,24 +204,22 @@ class sram_base(design, verilog, lef):
|
|||
names=self.control_bus_names[port],
|
||||
length=self.vertical_bus_height)
|
||||
|
||||
self.addr_bus_names=["A{0}[{1}]".format(port,i) for i in range(self.addr_size)]
|
||||
self.addr_bus_names=["A{0}[{1}]".format(port, i) for i in range(self.addr_size)]
|
||||
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=self.addr_bus_offset,
|
||||
names=self.addr_bus_names,
|
||||
length=self.addr_bus_height))
|
||||
|
||||
|
||||
self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port,i) for i in range(self.num_banks)]
|
||||
self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port, i) for i in range(self.num_banks)]
|
||||
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=self.bank_sel_bus_offset,
|
||||
names=self.bank_sel_bus_names,
|
||||
length=self.vertical_bus_height))
|
||||
|
||||
|
||||
# Horizontal data bus
|
||||
self.data_bus_names = ["DATA{0}[{1}]".format(port,i) for i in range(self.word_size)]
|
||||
self.data_bus_names = ["DATA{0}[{1}]".format(port, i) for i in range(self.word_size)]
|
||||
self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3",
|
||||
pitch=self.m3_pitch,
|
||||
offset=self.data_bus_offset,
|
||||
|
|
@ -249,7 +238,7 @@ class sram_base(design, verilog, lef):
|
|||
# the decoder in 4-bank SRAMs
|
||||
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
|
||||
pitch=self.m1_pitch,
|
||||
offset=self.supply_bus_offset+vector(0,self.m1_pitch),
|
||||
offset=self.supply_bus_offset + vector(0, self.m1_pitch),
|
||||
names=["gnd"],
|
||||
length=self.supply_bus_width))
|
||||
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
|
||||
|
|
@ -258,20 +247,17 @@ class sram_base(design, verilog, lef):
|
|||
names=self.control_bus_names[port],
|
||||
length=self.control_bus_width))
|
||||
|
||||
|
||||
|
||||
def add_multi_bank_modules(self):
|
||||
""" Create the multibank address flops and bank decoder """
|
||||
from dff_buf_array import dff_buf_array
|
||||
self.msb_address = dff_buf_array(name="msb_address",
|
||||
rows=1,
|
||||
columns=self.num_banks/2)
|
||||
columns=self.num_banks / 2)
|
||||
self.add_mod(self.msb_address)
|
||||
|
||||
if self.num_banks>2:
|
||||
self.msb_decoder = self.bank.decoder.pre2_4
|
||||
self.add_mod(self.msb_decoder)
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
|
|
@ -293,7 +279,6 @@ class sram_base(design, verilog, lef):
|
|||
if self.write_size:
|
||||
self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks)
|
||||
self.add_mod(self.wmask_dff)
|
||||
|
||||
|
||||
# Create the bank module (up to four are instantiated)
|
||||
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
|
||||
|
|
@ -305,7 +290,8 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
self.bank_count = 0
|
||||
|
||||
#The control logic can resize itself based on the other modules. Requires all other modules added before control logic.
|
||||
# The control logic can resize itself based on the other modules.
|
||||
# Requires all other modules added before control logic.
|
||||
self.all_mods_except_control_done = True
|
||||
|
||||
c = reload(__import__(OPTS.control_logic))
|
||||
|
|
@ -320,40 +306,40 @@ class sram_base(design, verilog, lef):
|
|||
port_type="rw")
|
||||
self.add_mod(self.control_logic_rw)
|
||||
if len(self.writeonly_ports)>0:
|
||||
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
|
||||
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
|
||||
words_per_row=self.words_per_row,
|
||||
word_size=self.word_size,
|
||||
sram=self,
|
||||
port_type="w")
|
||||
self.add_mod(self.control_logic_w)
|
||||
if len(self.readonly_ports)>0:
|
||||
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
|
||||
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
|
||||
words_per_row=self.words_per_row,
|
||||
word_size=self.word_size,
|
||||
sram=self,
|
||||
port_type="r")
|
||||
self.add_mod(self.control_logic_r)
|
||||
|
||||
def create_bank(self,bank_num):
|
||||
""" Create a bank """
|
||||
def create_bank(self, bank_num):
|
||||
""" Create a bank """
|
||||
self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num),
|
||||
mod=self.bank))
|
||||
|
||||
temp = []
|
||||
for port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
temp.append("dout{0}[{1}]".format(port,bit))
|
||||
temp.append("dout{0}[{1}]".format(port, bit))
|
||||
for port in self.all_ports:
|
||||
temp.append("rbl_bl{0}".format(port))
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
temp.append("bank_din{0}[{1}]".format(port,bit))
|
||||
temp.append("bank_din{0}[{1}]".format(port, bit))
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.bank_addr_size):
|
||||
temp.append("a{0}[{1}]".format(port,bit))
|
||||
temp.append("a{0}[{1}]".format(port, bit))
|
||||
if(self.num_banks > 1):
|
||||
for port in self.all_ports:
|
||||
temp.append("bank_sel{0}[{1}]".format(port,bank_num))
|
||||
temp.append("bank_sel{0}[{1}]".format(port, bank_num))
|
||||
for port in self.read_ports:
|
||||
temp.append("s_en{0}".format(port))
|
||||
for port in self.all_ports:
|
||||
|
|
@ -369,7 +355,6 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
return self.bank_insts[-1]
|
||||
|
||||
|
||||
def place_bank(self, bank_inst, position, x_flip, y_flip):
|
||||
""" Place a bank at the given position with orientations """
|
||||
|
||||
|
|
@ -400,7 +385,6 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
return bank_inst
|
||||
|
||||
|
||||
def create_row_addr_dff(self):
|
||||
""" Add all address flops for the main decoder """
|
||||
insts = []
|
||||
|
|
@ -412,13 +396,12 @@ class sram_base(design, verilog, lef):
|
|||
inputs = []
|
||||
outputs = []
|
||||
for bit in range(self.row_addr_size):
|
||||
inputs.append("addr{}[{}]".format(port,bit+self.col_addr_size))
|
||||
outputs.append("a{}[{}]".format(port,bit+self.col_addr_size))
|
||||
inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size))
|
||||
outputs.append("a{}[{}]".format(port, bit + self.col_addr_size))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
||||
|
||||
return insts
|
||||
|
||||
|
||||
def create_col_addr_dff(self):
|
||||
""" Add and place all address flops for the column decoder """
|
||||
|
|
@ -431,14 +414,13 @@ class sram_base(design, verilog, lef):
|
|||
inputs = []
|
||||
outputs = []
|
||||
for bit in range(self.col_addr_size):
|
||||
inputs.append("addr{}[{}]".format(port,bit))
|
||||
outputs.append("a{}[{}]".format(port,bit))
|
||||
inputs.append("addr{}[{}]".format(port, bit))
|
||||
outputs.append("a{}[{}]".format(port, bit))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
||||
|
||||
return insts
|
||||
|
||||
|
||||
def create_data_dff(self):
|
||||
""" Add and place all data flops """
|
||||
insts = []
|
||||
|
|
@ -454,8 +436,8 @@ class sram_base(design, verilog, lef):
|
|||
inputs = []
|
||||
outputs = []
|
||||
for bit in range(self.word_size):
|
||||
inputs.append("din{}[{}]".format(port,bit))
|
||||
outputs.append("bank_din{}[{}]".format(port,bit))
|
||||
inputs.append("din{}[{}]".format(port, bit))
|
||||
outputs.append("bank_din{}[{}]".format(port, bit))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
||||
|
||||
|
|
@ -483,7 +465,6 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
return insts
|
||||
|
||||
|
||||
def create_control_logic(self):
|
||||
""" Add control logic instances """
|
||||
|
||||
|
|
@ -516,12 +497,13 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
return insts
|
||||
|
||||
|
||||
def connect_vbus_m2m3(self, src_pin, dest_pin):
|
||||
""" Helper routine to connect an instance to a vertical bus.
|
||||
"""
|
||||
Helper routine to connect an instance to a vertical bus.
|
||||
Routes horizontal then vertical L shape.
|
||||
Dest pin is assumed to be on M2.
|
||||
Src pin can be on M1/M2/M3."""
|
||||
Src pin can be on M1/M2/M3.
|
||||
"""
|
||||
|
||||
if src_pin.cx()<dest_pin.cx():
|
||||
in_pos = src_pin.rc()
|
||||
|
|
@ -533,16 +515,52 @@ class sram_base(design, verilog, lef):
|
|||
out_pos = dest_pin.uc()
|
||||
|
||||
# move horizontal first
|
||||
self.add_wire(("m3","via2","m2"),[in_pos, vector(out_pos.x,in_pos.y),out_pos])
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[in_pos,
|
||||
vector(out_pos.x, in_pos.y),
|
||||
out_pos])
|
||||
if src_pin.layer=="m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=in_pos)
|
||||
if src_pin.layer in ["m1","m2"]:
|
||||
if src_pin.layer in ["m1", "m2"]:
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=in_pos)
|
||||
|
||||
|
||||
def connect_hbus_m2m3(self, src_pin, dest_pin):
|
||||
"""
|
||||
Helper routine to connect an instance to a horizontal bus.
|
||||
Routes horizontal then vertical L shape.
|
||||
Dest pin is on M1/M2/M3.
|
||||
Src pin can be on M1/M2/M3.
|
||||
"""
|
||||
|
||||
if src_pin.cx()<dest_pin.cx():
|
||||
in_pos = src_pin.rc()
|
||||
else:
|
||||
in_pos = src_pin.lc()
|
||||
if src_pin.cy() < dest_pin.cy():
|
||||
out_pos = dest_pin.lc()
|
||||
else:
|
||||
out_pos = dest_pin.rc()
|
||||
|
||||
# move horizontal first
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[in_pos,
|
||||
vector(out_pos.x, in_pos.y),
|
||||
out_pos])
|
||||
if src_pin.layer=="m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=in_pos)
|
||||
if src_pin.layer in ["m1", "m2"]:
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=in_pos)
|
||||
if dest_pin.layer=="m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=out_pos)
|
||||
if dest_pin.layer=="m3":
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=out_pos)
|
||||
|
||||
def sp_write(self, sp_name):
|
||||
# Write the entire spice of the object to the file
|
||||
############################################################
|
||||
|
|
@ -571,40 +589,39 @@ class sram_base(design, verilog, lef):
|
|||
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
|
||||
stage_effort_list = []
|
||||
|
||||
#Clk_buf originates from the control logic so only the bank is related to the wordline path
|
||||
external_wordline_cout = 0 #No loading on the wordline other than in the bank.
|
||||
# Clk_buf originates from the control logic so only the bank is related to the wordline path
|
||||
# No loading on the wordline other than in the bank.
|
||||
external_wordline_cout = 0
|
||||
stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_wl_en_cin(self):
|
||||
"""Gets the capacitive load the of clock (clk_buf) for the sram"""
|
||||
#Only the wordline drivers within the bank use this signal
|
||||
# Only the wordline drivers within the bank use this signal
|
||||
return self.bank.get_wl_en_cin()
|
||||
|
||||
def get_w_en_cin(self):
|
||||
"""Gets the capacitive load the of write enable (w_en) for the sram"""
|
||||
#Only the write drivers within the bank use this signal
|
||||
# Only the write drivers within the bank use this signal
|
||||
return self.bank.get_w_en_cin()
|
||||
|
||||
|
||||
def get_p_en_bar_cin(self):
|
||||
"""Gets the capacitive load the of precharge enable (p_en_bar) for the sram"""
|
||||
#Only the precharges within the bank use this signal
|
||||
# Only the precharges within the bank use this signal
|
||||
return self.bank.get_p_en_bar_cin()
|
||||
|
||||
def get_clk_bar_cin(self):
|
||||
"""Gets the capacitive load the of clock (clk_buf_bar) for the sram"""
|
||||
#As clk_buf_bar is an output of the control logic. The cap for that module is not determined here.
|
||||
#Only the precharge cells use this signal (other than the control logic)
|
||||
# As clk_buf_bar is an output of the control logic. The cap for that module is not determined here.
|
||||
# Only the precharge cells use this signal (other than the control logic)
|
||||
return self.bank.get_clk_bar_cin()
|
||||
|
||||
def get_sen_cin(self):
|
||||
"""Gets the capacitive load the of sense amp enable for the sram"""
|
||||
#Only the sense_amps use this signal (other than the control logic)
|
||||
# Only the sense_amps use this signal (other than the control logic)
|
||||
return self.bank.get_sen_cin()
|
||||
|
||||
|
||||
def get_dff_clk_buf_cin(self):
|
||||
"""Get the relative capacitance of the clk_buf signal.
|
||||
Does not get the control logic loading but everything else"""
|
||||
|
|
|
|||
Loading…
Reference in New Issue