mirror of https://github.com/VLSIDA/OpenRAM.git
Fixed merge conflict, moved control logic mod instantiation, removed some commented out code.
This commit is contained in:
commit
e9f6566e59
|
|
@ -69,31 +69,31 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
"""Checks both DRC and LVS for a module"""
|
"""Checks both DRC and LVS for a module"""
|
||||||
# Unit tests will check themselves.
|
# Unit tests will check themselves.
|
||||||
# Do not run if disabled in options.
|
# Do not run if disabled in options.
|
||||||
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
|
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
tempspice = OPTS.openram_temp + "/temp.sp"
|
tempspice = OPTS.openram_temp + "/temp.sp"
|
||||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||||
self.sp_write(tempspice)
|
self.sp_write(tempspice)
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name))
|
debug.check(verify.run_drc(self.name, tempgds, final_verification) == 0,"DRC failed for {0}".format(self.name))
|
||||||
debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name))
|
debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name))
|
||||||
os.remove(tempspice)
|
os.remove(tempspice)
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
def DRC(self):
|
def DRC(self, final_verification=False):
|
||||||
"""Checks DRC for a module"""
|
"""Checks DRC for a module"""
|
||||||
# Unit tests will check themselves.
|
# Unit tests will check themselves.
|
||||||
# Do not run if disabled in options.
|
# Do not run if disabled in options.
|
||||||
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
|
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name))
|
debug.check(verify.run_drc(self.name, tempgds, final_verification) == 0,"DRC failed for {0}".format(self.name))
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
def LVS(self, final_verification=False):
|
def LVS(self, final_verification=False):
|
||||||
"""Checks LVS for a module"""
|
"""Checks LVS for a module"""
|
||||||
# Unit tests will check themselves.
|
# Unit tests will check themselves.
|
||||||
# Do not run if disabled in options.
|
# Do not run if disabled in options.
|
||||||
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
|
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
tempspice = OPTS.openram_temp + "/temp.sp"
|
tempspice = OPTS.openram_temp + "/temp.sp"
|
||||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||||
self.sp_write(tempspice)
|
self.sp_write(tempspice)
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,10 @@ def parse_args():
|
||||||
help="Base output file name(s) prefix", metavar="FILE"),
|
help="Base output file name(s) prefix", metavar="FILE"),
|
||||||
optparse.make_option("-p", "--outpath", dest="output_path",
|
optparse.make_option("-p", "--outpath", dest="output_path",
|
||||||
help="Output file(s) location"),
|
help="Output file(s) location"),
|
||||||
|
optparse.make_option("-i", "--inlinecheck", action="store_true",
|
||||||
|
help="Enable inline LVS/DRC checks", dest="inline_lvsdrc"),
|
||||||
optparse.make_option("-n", "--nocheck", action="store_false",
|
optparse.make_option("-n", "--nocheck", action="store_false",
|
||||||
help="Disable inline LVS/DRC checks", dest="check_lvsdrc"),
|
help="Disable all LVS/DRC checks", dest="check_lvsdrc"),
|
||||||
optparse.make_option("-v", "--verbose", action="count", dest="debug_level",
|
optparse.make_option("-v", "--verbose", action="count", dest="debug_level",
|
||||||
help="Increase the verbosity level"),
|
help="Increase the verbosity level"),
|
||||||
optparse.make_option("-t", "--tech", dest="tech_name",
|
optparse.make_option("-t", "--tech", dest="tech_name",
|
||||||
|
|
@ -387,10 +389,10 @@ def import_tech():
|
||||||
def print_time(name, now_time, last_time=None):
|
def print_time(name, now_time, last_time=None):
|
||||||
""" Print a statement about the time delta. """
|
""" Print a statement about the time delta. """
|
||||||
if last_time:
|
if last_time:
|
||||||
time = round((now_time-last_time).total_seconds(),1)
|
time = str(round((now_time-last_time).total_seconds(),1)) + " seconds"
|
||||||
else:
|
else:
|
||||||
time = now_time
|
time = now_time.strftime('%m/%d/%Y %H:%M:%S')
|
||||||
print("** {0}: {1} seconds".format(name,time))
|
print("** {0}: {1}".format(name,time))
|
||||||
|
|
||||||
|
|
||||||
def report_status():
|
def report_status():
|
||||||
|
|
@ -413,6 +415,9 @@ def report_status():
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
print("Netlist only mode (no physical design is being done).")
|
print("Netlist only mode (no physical design is being done).")
|
||||||
|
|
||||||
if not OPTS.check_lvsdrc:
|
if not OPTS.inline_lvsdrc:
|
||||||
print("DRC/LVS/PEX checking is disabled.")
|
print("DRC/LVS/PEX is only run on the top-level design.")
|
||||||
|
|
||||||
|
if not OPTS.check_lvsdrc:
|
||||||
|
print("DRC/LVS/PEX is completely disabled.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ class bank(design.design):
|
||||||
Dynamically generated a single bank including bitcell array,
|
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.
|
write driver and sense amplifiers.
|
||||||
|
This can create up to two ports in any combination: rw, w, r.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sram_config, name=""):
|
def __init__(self, sram_config, name=""):
|
||||||
|
|
@ -30,7 +31,6 @@ class bank(design.design):
|
||||||
design.design.__init__(self, name)
|
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,
|
# 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
|
# so this prefix will be added to all of the input signals to create
|
||||||
# the internal gated signals.
|
# the internal gated signals.
|
||||||
|
|
@ -41,6 +41,7 @@ class bank(design.design):
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
|
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,11 +49,11 @@ class bank(design.design):
|
||||||
self.compute_sizes()
|
self.compute_sizes()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.setup_routing_constraints()
|
self.setup_routing_constraints()
|
||||||
self.route_layout()
|
self.route_layout()
|
||||||
|
|
||||||
|
|
@ -96,109 +97,235 @@ class bank(design.design):
|
||||||
def route_layout(self):
|
def route_layout(self):
|
||||||
""" Create routing amoung the modules """
|
""" Create routing amoung the modules """
|
||||||
self.route_central_bus()
|
self.route_central_bus()
|
||||||
self.route_precharge_to_bitcell_array()
|
|
||||||
self.route_col_mux_to_precharge_array()
|
for port in self.all_ports:
|
||||||
self.route_sense_amp_to_col_mux_or_precharge_array()
|
self.route_bitlines(port)
|
||||||
self.route_write_driver_to_sense_amp()
|
self.route_wordline_driver(port)
|
||||||
self.route_sense_amp_out()
|
self.route_row_decoder(port)
|
||||||
self.route_wordline_driver()
|
self.route_column_address_lines(port)
|
||||||
self.route_write_driver()
|
self.route_control_lines(port)
|
||||||
self.route_row_decoder()
|
|
||||||
self.route_column_address_lines()
|
|
||||||
self.route_control_lines()
|
|
||||||
if self.num_banks > 1:
|
if self.num_banks > 1:
|
||||||
self.route_bank_select()
|
self.route_bank_select(port)
|
||||||
|
|
||||||
self.route_supplies()
|
self.route_supplies()
|
||||||
|
|
||||||
def create_modules(self):
|
def route_bitlines(self, port):
|
||||||
""" Add modules. The order should not matter! """
|
""" Route the bitlines depending on the port type rw, w, or r. """
|
||||||
|
|
||||||
|
if port in self.readwrite_ports:
|
||||||
|
# write_driver -> sense_amp -> (column_mux) -> precharge -> bitcell_array
|
||||||
|
self.route_write_driver_in(port)
|
||||||
|
self.route_sense_amp_out(port)
|
||||||
|
self.route_write_driver_to_sense_amp(port)
|
||||||
|
self.route_sense_amp_to_column_mux_or_precharge_array(port)
|
||||||
|
self.route_column_mux_to_precharge_array(port)
|
||||||
|
self.route_precharge_to_bitcell_array(port)
|
||||||
|
elif port in self.read_ports:
|
||||||
|
# sense_amp -> (column_mux) -> precharge -> bitcell_array
|
||||||
|
self.route_sense_amp_out(port)
|
||||||
|
self.route_sense_amp_to_column_mux_or_precharge_array(port)
|
||||||
|
self.route_column_mux_to_precharge_array(port)
|
||||||
|
self.route_precharge_to_bitcell_array(port)
|
||||||
|
else:
|
||||||
|
# write_driver -> (column_mux) -> bitcell_array
|
||||||
|
self.route_write_driver_in(port)
|
||||||
|
self.route_write_driver_to_column_mux_or_bitcell_array(port)
|
||||||
|
self.route_column_mux_to_bitcell_array(port)
|
||||||
|
|
||||||
|
def create_instances(self):
|
||||||
|
""" Create the instances of the netlist. """
|
||||||
|
|
||||||
# Above the bitcell array
|
|
||||||
self.create_bitcell_array()
|
self.create_bitcell_array()
|
||||||
self.create_precharge_array()
|
|
||||||
|
|
||||||
# Below the bitcell array
|
self.create_precharge_array()
|
||||||
self.create_column_mux_array()
|
self.create_column_mux_array()
|
||||||
self.create_sense_amp_array()
|
self.create_sense_amp_array()
|
||||||
self.create_write_driver_array()
|
self.create_write_driver_array()
|
||||||
|
|
||||||
# To the left of the bitcell array
|
|
||||||
self.create_row_decoder()
|
self.create_row_decoder()
|
||||||
self.create_wordline_driver()
|
self.create_wordline_driver()
|
||||||
self.create_column_decoder()
|
self.create_column_decoder()
|
||||||
|
|
||||||
self.create_bank_select()
|
self.create_bank_select()
|
||||||
|
|
||||||
def compute_module_offsets(self):
|
def compute_instance_offsets(self):
|
||||||
"""
|
"""
|
||||||
Compute the module offsets.
|
Compute the empty instance offsets for port0 and port1 (if needed)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# These are created even if the port type (e.g. read only)
|
||||||
|
# doesn't need the instance (e.g. write driver).
|
||||||
|
|
||||||
|
# Create the bottom-up and left to right order of components in each port
|
||||||
|
# which deepends on the port type rw, w, r
|
||||||
|
self.vertical_port_order = []
|
||||||
|
self.vertical_port_offsets = []
|
||||||
|
for port in self.all_ports:
|
||||||
|
self.vertical_port_order.append([])
|
||||||
|
self.vertical_port_offsets.append([None]*4)
|
||||||
|
|
||||||
|
# For later placement, these are fixed in the order: write driver,
|
||||||
|
# sense amp, clumn mux, precharge, even if the item is not used
|
||||||
|
# in a given port (it will be None then)
|
||||||
|
self.vertical_port_order[port].append(self.write_driver_array_inst[port])
|
||||||
|
self.vertical_port_order[port].append(self.sense_amp_array_inst[port])
|
||||||
|
self.vertical_port_order[port].append(self.column_mux_array_inst[port])
|
||||||
|
self.vertical_port_order[port].append(self.precharge_array_inst[port])
|
||||||
|
|
||||||
|
# For the odd ones they will go on top, so reverse in place
|
||||||
|
if port%2:
|
||||||
|
self.vertical_port_order[port]=self.vertical_port_order[port][::-1]
|
||||||
|
|
||||||
|
self.write_driver_offsets = [None]*len(self.all_ports)
|
||||||
|
self.sense_amp_offsets = [None]*len(self.all_ports)
|
||||||
|
self.column_mux_offsets = [None]*len(self.all_ports)
|
||||||
|
self.precharge_offsets = [None]*len(self.all_ports)
|
||||||
|
|
||||||
|
self.wordline_driver_offsets = [None]*len(self.all_ports)
|
||||||
|
self.row_decoder_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.compute_instance_port0_offsets()
|
||||||
|
if len(self.all_ports)==2:
|
||||||
|
self.compute_instance_port1_offsets()
|
||||||
|
|
||||||
|
|
||||||
|
def compute_instance_port0_offsets(self):
|
||||||
|
"""
|
||||||
|
Compute the instance offsets for port0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
port = 0
|
||||||
|
|
||||||
# UPPER RIGHT QUADRANT
|
# UPPER RIGHT QUADRANT
|
||||||
# Bitcell array is placed at (0,0)
|
# Bitcell array is placed at (0,0)
|
||||||
self.bitcell_array_offset = vector(0,0)
|
self.bitcell_array_offset = vector(0,0)
|
||||||
|
|
||||||
# LOWER RIGHT QUADRANT
|
# LOWER RIGHT QUADRANT
|
||||||
# Below the bitcell array
|
# Below the bitcell array
|
||||||
y_offset = self.precharge_array[0].height + self.m2_gap
|
y_height = 0
|
||||||
self.precharge_offset = vector(0,-y_offset)
|
for p in self.vertical_port_order[port]:
|
||||||
if self.col_addr_size > 0:
|
if p==None:
|
||||||
y_offset += self.column_mux_array[0].height + self.m2_gap
|
continue
|
||||||
self.column_mux_offset = vector(0,-y_offset)
|
y_height += p.height + self.m2_gap
|
||||||
y_offset += self.sense_amp_array.height + self.m2_gap
|
|
||||||
self.sense_amp_offset = vector(0,-y_offset)
|
y_offset = -y_height
|
||||||
y_offset += self.write_driver_array.height + self.m2_gap
|
for i,p in enumerate(self.vertical_port_order[port]):
|
||||||
self.write_driver_offset = vector(0,-y_offset)
|
if p==None:
|
||||||
|
continue
|
||||||
|
self.vertical_port_offsets[port][i]=vector(0,y_offset)
|
||||||
|
y_offset += (p.height + self.m2_gap)
|
||||||
|
|
||||||
|
self.write_driver_offsets[port] = self.vertical_port_offsets[port][0]
|
||||||
|
self.sense_amp_offsets[port] = self.vertical_port_offsets[port][1]
|
||||||
|
self.column_mux_offsets[port] = self.vertical_port_offsets[port][2]
|
||||||
|
self.precharge_offsets[port] = self.vertical_port_offsets[port][3]
|
||||||
|
|
||||||
# UPPER LEFT QUADRANT
|
# UPPER LEFT QUADRANT
|
||||||
# To the left of the bitcell array
|
# To the left of the bitcell array
|
||||||
# The wordline driver is placed to the right of the main decoder width.
|
# The wordline driver is placed to the right of the main decoder width.
|
||||||
x_offset = self.central_bus_width + self.wordline_driver.width - self.m2_pitch
|
x_offset = self.central_bus_width + self.wordline_driver.width
|
||||||
self.wordline_driver_offset = vector(-x_offset,0)
|
self.wordline_driver_offsets[port] = vector(-x_offset,0)
|
||||||
x_offset += self.row_decoder.width + self.m2_pitch
|
x_offset += self.row_decoder.width + self.m2_gap
|
||||||
self.row_decoder_offset = vector(-x_offset,0)
|
self.row_decoder_offsets[port] = vector(-x_offset,0)
|
||||||
|
|
||||||
# LOWER LEFT QUADRANT
|
# LOWER LEFT QUADRANT
|
||||||
# Place the col decoder right aligned with row decoder (x_offset doesn't change)
|
# Place the col decoder right aligned with row decoder (x_offset doesn't change)
|
||||||
# Below the bitcell array
|
# Below the bitcell array
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
y_offset = self.col_decoder.height
|
y_offset = self.column_decoder.height
|
||||||
else:
|
else:
|
||||||
y_offset = 0
|
y_offset = 0
|
||||||
y_offset += 2*drc("well_to_well")
|
y_offset += 2*drc("well_to_well")
|
||||||
self.column_decoder_offset = 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)
|
# Bank select gets placed below the column decoder (x_offset doesn't change)
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
y_offset = min(self.column_decoder_offset.y, self.column_mux_offset.y)
|
y_offset = min(self.column_decoder_offsets[port].y, self.column_mux_offsets[port].y)
|
||||||
else:
|
else:
|
||||||
y_offset = self.row_decoder_offset.y
|
y_offset = self.row_decoder_offsets[port].y
|
||||||
if self.num_banks > 1:
|
if self.num_banks > 1:
|
||||||
y_offset += self.bank_select.height + drc("well_to_well")
|
y_offset += self.bank_select.height + drc("well_to_well")
|
||||||
self.bank_select_offset = vector(-x_offset,-y_offset)
|
self.bank_select_offsets[port] = vector(-x_offset,-y_offset)
|
||||||
|
|
||||||
def place_modules(self):
|
def compute_instance_port1_offsets(self):
|
||||||
""" Place the modules. """
|
"""
|
||||||
|
Compute the instance offsets for port1 on the top of the bank.
|
||||||
|
"""
|
||||||
|
|
||||||
self.compute_module_offsets()
|
port=1
|
||||||
|
|
||||||
|
# The center point for these cells are the upper-right corner of
|
||||||
|
# the bitcell array.
|
||||||
|
# The decoder/driver logic is placed on the right and mirrored on Y-axis.
|
||||||
|
# The write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
|
||||||
|
|
||||||
|
# LOWER LEFT QUADRANT
|
||||||
|
# Bitcell array is placed at (0,0)
|
||||||
|
|
||||||
|
# UPPER LEFT QUADRANT
|
||||||
|
# Above the bitcell array
|
||||||
|
y_offset = self.bitcell_array.height + self.m2_gap
|
||||||
|
for i,p in enumerate(self.vertical_port_order[port]):
|
||||||
|
if p==None:
|
||||||
|
continue
|
||||||
|
y_offset += (p.height + self.m2_gap)
|
||||||
|
self.vertical_port_offsets[port][i]=vector(0,y_offset)
|
||||||
|
|
||||||
|
# Reversed order
|
||||||
|
self.write_driver_offsets[port] = self.vertical_port_offsets[port][3]
|
||||||
|
self.sense_amp_offsets[port] = self.vertical_port_offsets[port][2]
|
||||||
|
self.column_mux_offsets[port] = self.vertical_port_offsets[port][1]
|
||||||
|
self.precharge_offsets[port] = self.vertical_port_offsets[port][0]
|
||||||
|
|
||||||
|
# LOWER RIGHT QUADRANT
|
||||||
|
# To the left of the bitcell array
|
||||||
|
# The wordline driver is placed to the right of the main decoder width.
|
||||||
|
x_offset = self.bitcell_array.width + self.central_bus_width + self.wordline_driver.width
|
||||||
|
self.wordline_driver_offsets[port] = vector(x_offset,0)
|
||||||
|
x_offset += self.row_decoder.width + self.m2_gap
|
||||||
|
self.row_decoder_offsets[port] = vector(x_offset,0)
|
||||||
|
|
||||||
|
# UPPER RIGHT QUADRANT
|
||||||
|
# Place the col decoder right aligned with row decoder (x_offset doesn't change)
|
||||||
|
# Below the bitcell array
|
||||||
|
y_offset = self.bitcell_array.height + self.m2_gap
|
||||||
|
y_offset += 2*drc("well_to_well")
|
||||||
|
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:
|
||||||
|
y_offset = max(self.column_decoder_offsets[port].y + self.column_decoder.height,
|
||||||
|
self.column_mux_offsets[port].y + self.column_mux_array[port].height)
|
||||||
|
else:
|
||||||
|
y_offset = self.row_decoder_offsets[port].y
|
||||||
|
self.bank_select_offsets[port] = vector(x_offset,y_offset)
|
||||||
|
|
||||||
|
def place_instances(self):
|
||||||
|
""" Place the instances. """
|
||||||
|
|
||||||
|
self.compute_instance_offsets()
|
||||||
|
|
||||||
# UPPER RIGHT QUADRANT
|
# UPPER RIGHT QUADRANT
|
||||||
self.place_bitcell_array(self.bitcell_array_offset)
|
self.place_bitcell_array(self.bitcell_array_offset)
|
||||||
|
|
||||||
# LOWER RIGHT QUADRANT
|
# LOWER RIGHT QUADRANT
|
||||||
self.place_precharge_array([self.precharge_offset]*len(self.read_ports))
|
# These are fixed in the order: write driver, sense amp, clumn mux, precharge,
|
||||||
self.place_column_mux_array([self.column_mux_offset]*len(self.all_ports))
|
# even if the item is not used in a given port (it will be None then)
|
||||||
self.place_sense_amp_array([self.sense_amp_offset]*len(self.read_ports))
|
self.place_write_driver_array(self.write_driver_offsets)
|
||||||
self.place_write_driver_array([self.write_driver_offset]*len(self.write_ports))
|
self.place_sense_amp_array(self.sense_amp_offsets)
|
||||||
|
self.place_column_mux_array(self.column_mux_offsets)
|
||||||
|
self.place_precharge_array(self.precharge_offsets)
|
||||||
|
|
||||||
# UPPER LEFT QUADRANT
|
# UPPER LEFT QUADRANT
|
||||||
self.place_row_decoder([self.row_decoder_offset]*len(self.all_ports))
|
self.place_row_decoder(self.row_decoder_offsets)
|
||||||
self.place_wordline_driver([self.wordline_driver_offset]*len(self.all_ports))
|
self.place_wordline_driver(self.wordline_driver_offsets)
|
||||||
|
|
||||||
# LOWER LEFT QUADRANT
|
# LOWER LEFT QUADRANT
|
||||||
self.place_column_decoder([self.column_decoder_offset]*len(self.all_ports))
|
self.place_column_decoder(self.column_decoder_offsets)
|
||||||
self.place_bank_select([self.bank_select_offset]*len(self.all_ports))
|
self.place_bank_select(self.bank_select_offsets)
|
||||||
|
|
||||||
|
|
||||||
def compute_sizes(self):
|
def compute_sizes(self):
|
||||||
|
|
@ -249,7 +376,7 @@ class bank(design.design):
|
||||||
|
|
||||||
# The width of this bus is needed to place other modules (e.g. decoder)
|
# The width of this bus is needed to place other modules (e.g. decoder)
|
||||||
# A width on each side too
|
# A width on each side too
|
||||||
self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width
|
self.central_bus_width = self.m2_pitch * self.num_control_lines + self.m2_width
|
||||||
|
|
||||||
# A space for wells or jogging m2
|
# A space for wells or jogging m2
|
||||||
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
|
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
|
||||||
|
|
@ -257,7 +384,7 @@ class bank(design.design):
|
||||||
|
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Create all the modules using the class loader """
|
""" Add all the modules using the class loader """
|
||||||
|
|
||||||
mod_list = ["bitcell", "decoder", "wordline_driver",
|
mod_list = ["bitcell", "decoder", "wordline_driver",
|
||||||
"bitcell_array", "sense_amp_array", "precharge_array",
|
"bitcell_array", "sense_amp_array", "precharge_array",
|
||||||
|
|
@ -351,10 +478,10 @@ class bank(design.design):
|
||||||
def create_precharge_array(self):
|
def create_precharge_array(self):
|
||||||
""" Creating Precharge """
|
""" Creating Precharge """
|
||||||
|
|
||||||
self.precharge_array_inst = []
|
self.precharge_array_inst = [None]*len(self.all_ports)
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(port),
|
self.precharge_array_inst[port]=self.add_inst(name="precharge_array{}".format(port),
|
||||||
mod=self.precharge_array[port]))
|
mod=self.precharge_array[port])
|
||||||
temp = []
|
temp = []
|
||||||
for i in range(self.num_cols):
|
for i in range(self.num_cols):
|
||||||
temp.append(self.bl_names[port]+"_{0}".format(i))
|
temp.append(self.bl_names[port]+"_{0}".format(i))
|
||||||
|
|
@ -368,21 +495,24 @@ class bank(design.design):
|
||||||
|
|
||||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place precharge array.")
|
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place precharge array.")
|
||||||
|
|
||||||
# FIXME: place for multiport
|
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
self.precharge_array_inst[port].place(offsets[port])
|
if port%2 == 1:
|
||||||
|
mirror = "MX"
|
||||||
|
else:
|
||||||
|
mirror = "R0"
|
||||||
|
self.precharge_array_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
def create_column_mux_array(self):
|
def create_column_mux_array(self):
|
||||||
""" Creating Column Mux when words_per_row > 1 . """
|
""" Creating Column Mux when words_per_row > 1 . """
|
||||||
self.col_mux_array_inst = []
|
self.column_mux_array_inst = [None]*len(self.all_ports)
|
||||||
|
|
||||||
if self.col_addr_size == 0:
|
if self.col_addr_size == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port),
|
self.column_mux_array_inst[port] = self.add_inst(name="column_mux_array{}".format(port),
|
||||||
mod=self.column_mux_array[port]))
|
mod=self.column_mux_array[port])
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for col in range(self.num_cols):
|
for col in range(self.num_cols):
|
||||||
|
|
@ -406,16 +536,20 @@ class bank(design.design):
|
||||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column mux array.")
|
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column mux array.")
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.col_mux_array_inst[port].place(offsets[port])
|
if port%2 == 1:
|
||||||
|
mirror = "MX"
|
||||||
|
else:
|
||||||
|
mirror = "R0"
|
||||||
|
self.column_mux_array_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
def create_sense_amp_array(self):
|
def create_sense_amp_array(self):
|
||||||
""" Creating Sense amp """
|
""" Creating Sense amp """
|
||||||
|
|
||||||
self.sense_amp_array_inst = []
|
self.sense_amp_array_inst = [None]*len(self.all_ports)
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(port),
|
self.sense_amp_array_inst[port] = self.add_inst(name="sense_amp_array{}".format(port),
|
||||||
mod=self.sense_amp_array))
|
mod=self.sense_amp_array)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
|
|
@ -435,23 +569,21 @@ class bank(design.design):
|
||||||
""" Placing Sense amp """
|
""" Placing Sense amp """
|
||||||
|
|
||||||
debug.check(len(offsets)>=len(self.read_ports), "Insufficient offsets to place sense amp array.")
|
debug.check(len(offsets)>=len(self.read_ports), "Insufficient offsets to place sense amp array.")
|
||||||
|
|
||||||
# FIXME: place for multiport
|
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
self.sense_amp_array_inst[port].place(offsets[port])
|
if port%2 == 1:
|
||||||
|
mirror = "MX"
|
||||||
|
else:
|
||||||
|
mirror = "R0"
|
||||||
|
self.sense_amp_array_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
def create_write_driver_array(self):
|
def create_write_driver_array(self):
|
||||||
""" Creating Write Driver """
|
""" Creating Write Driver """
|
||||||
|
|
||||||
self.write_driver_array_inst = []
|
self.write_driver_array_inst = [None]*len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.write_ports:
|
||||||
if port in self.write_ports:
|
self.write_driver_array_inst[port] = self.add_inst(name="write_driver_array{}".format(port),
|
||||||
self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port),
|
mod=self.write_driver_array)
|
||||||
mod=self.write_driver_array))
|
|
||||||
else:
|
|
||||||
self.write_driver_array_inst.append(None)
|
|
||||||
continue
|
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
|
|
@ -473,16 +605,20 @@ class bank(design.design):
|
||||||
debug.check(len(offsets)>=len(self.write_ports), "Insufficient offsets to place write driver array.")
|
debug.check(len(offsets)>=len(self.write_ports), "Insufficient offsets to place write driver array.")
|
||||||
|
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
self.write_driver_array_inst[port].place(offsets[port])
|
if port%2 == 1:
|
||||||
|
mirror = "MX"
|
||||||
|
else:
|
||||||
|
mirror = "R0"
|
||||||
|
self.write_driver_array_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
def create_row_decoder(self):
|
def create_row_decoder(self):
|
||||||
""" Create the hierarchical row decoder """
|
""" Create the hierarchical row decoder """
|
||||||
|
|
||||||
self.row_decoder_inst = []
|
self.row_decoder_inst = [None]*len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(port),
|
self.row_decoder_inst[port] = self.add_inst(name="row_decoder{}".format(port),
|
||||||
mod=self.row_decoder))
|
mod=self.row_decoder)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for bit in range(self.row_addr_size):
|
for bit in range(self.row_addr_size):
|
||||||
|
|
@ -504,18 +640,21 @@ class bank(design.design):
|
||||||
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
||||||
# The address flop and decoder are aligned in the x coord.
|
# The address flop and decoder are aligned in the x coord.
|
||||||
|
|
||||||
# FIXME: place for multiport
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.row_decoder_inst[port].place(offsets[port])
|
if port%2 == 1:
|
||||||
|
mirror = "MY"
|
||||||
|
else:
|
||||||
|
mirror = "R0"
|
||||||
|
self.row_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
def create_wordline_driver(self):
|
def create_wordline_driver(self):
|
||||||
""" Create the Wordline Driver """
|
""" Create the Wordline Driver """
|
||||||
|
|
||||||
self.wordline_driver_inst = []
|
self.wordline_driver_inst = [None]*len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(port),
|
self.wordline_driver_inst[port] = self.add_inst(name="wordline_driver{}".format(port),
|
||||||
mod=self.wordline_driver))
|
mod=self.wordline_driver)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for row in range(self.num_rows):
|
for row in range(self.num_rows):
|
||||||
|
|
@ -534,7 +673,11 @@ class bank(design.design):
|
||||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place wordline driver array.")
|
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place wordline driver array.")
|
||||||
|
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.wordline_driver_inst[port].place(offsets[port])
|
if port%2 == 1:
|
||||||
|
mirror = "MY"
|
||||||
|
else:
|
||||||
|
mirror = "R0"
|
||||||
|
self.wordline_driver_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
def create_column_decoder(self):
|
def create_column_decoder(self):
|
||||||
|
|
@ -545,20 +688,20 @@ class bank(design.design):
|
||||||
if self.col_addr_size == 0:
|
if self.col_addr_size == 0:
|
||||||
return
|
return
|
||||||
elif self.col_addr_size == 1:
|
elif self.col_addr_size == 1:
|
||||||
self.col_decoder = pinvbuf(height=self.mod_dff.height)
|
self.column_decoder = pinvbuf(height=self.mod_dff.height)
|
||||||
self.add_mod(self.col_decoder)
|
self.add_mod(self.column_decoder)
|
||||||
elif self.col_addr_size == 2:
|
elif self.col_addr_size == 2:
|
||||||
self.col_decoder = self.row_decoder.pre2_4
|
self.column_decoder = self.row_decoder.pre2_4
|
||||||
elif self.col_addr_size == 3:
|
elif self.col_addr_size == 3:
|
||||||
self.col_decoder = self.row_decoder.pre3_8
|
self.column_decoder = self.row_decoder.pre3_8
|
||||||
else:
|
else:
|
||||||
# No error checking before?
|
# No error checking before?
|
||||||
debug.error("Invalid column decoder?",-1)
|
debug.error("Invalid column decoder?",-1)
|
||||||
|
|
||||||
self.col_decoder_inst = []
|
self.column_decoder_inst = [None]*len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.col_decoder_inst.append(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.col_decoder))
|
mod=self.column_decoder)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
for bit in range(self.col_addr_size):
|
for bit in range(self.col_addr_size):
|
||||||
|
|
@ -579,7 +722,11 @@ class bank(design.design):
|
||||||
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:
|
for port in self.all_ports:
|
||||||
self.col_decoder_inst[port].place(offsets[port])
|
if port%2 == 1:
|
||||||
|
mirror = "MY"
|
||||||
|
else:
|
||||||
|
mirror = "R0"
|
||||||
|
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -589,10 +736,10 @@ class bank(design.design):
|
||||||
if not self.num_banks > 1:
|
if not self.num_banks > 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.bank_select_inst = []
|
self.bank_select_inst = [None]*len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.bank_select_inst.append(self.add_inst(name="bank_select{}".format(port),
|
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
|
||||||
mod=self.bank_select))
|
mod=self.bank_select)
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
temp.extend(self.input_control_signals[port])
|
temp.extend(self.input_control_signals[port])
|
||||||
|
|
@ -620,10 +767,9 @@ class bank(design.design):
|
||||||
self.copy_power_pins(inst,"vdd")
|
self.copy_power_pins(inst,"vdd")
|
||||||
self.copy_power_pins(inst,"gnd")
|
self.copy_power_pins(inst,"gnd")
|
||||||
|
|
||||||
def route_bank_select(self):
|
def route_bank_select(self, port):
|
||||||
""" Route the bank select logic. """
|
""" Route the bank select logic. """
|
||||||
|
|
||||||
for port in self.all_ports:
|
|
||||||
if self.port_id[port] == "rw":
|
if self.port_id[port] == "rw":
|
||||||
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"]
|
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"]
|
||||||
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"]
|
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"]
|
||||||
|
|
@ -642,7 +788,7 @@ class bank(design.design):
|
||||||
# Connect the inverter output to the central bus
|
# Connect the inverter output to the central bus
|
||||||
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
|
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
|
||||||
name = self.control_signals[port][signal]
|
name = self.control_signals[port][signal]
|
||||||
bus_pos = vector(self.bus_xoffset[name].x, out_pos.y)
|
bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y)
|
||||||
self.add_path("metal3",[out_pos, bus_pos])
|
self.add_path("metal3",[out_pos, bus_pos])
|
||||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||||
offset=bus_pos,
|
offset=bus_pos,
|
||||||
|
|
@ -660,32 +806,12 @@ class bank(design.design):
|
||||||
After the modules are instantiated, find the dimensions for the
|
After the modules are instantiated, find the dimensions for the
|
||||||
control bus, power ring, etc.
|
control bus, power ring, etc.
|
||||||
"""
|
"""
|
||||||
# FIXME: calculate for multiport
|
|
||||||
|
|
||||||
#The minimum point is either the bottom of the address flops,
|
self.max_y_offset = max([x.uy() for x in self.insts]) + 3*self.m1_width
|
||||||
#the column decoder (if there is one).
|
self.min_y_offset = min([x.by() for x in self.insts])
|
||||||
write_driver_min_y_offset = self.write_driver_array_inst[0].by() - 3*self.m2_pitch
|
|
||||||
row_decoder_min_y_offset = self.row_decoder_inst[0].by()
|
|
||||||
|
|
||||||
if self.col_addr_size > 0:
|
self.max_x_offset = max([x.rx() for x in self.insts]) + 3*self.m1_width
|
||||||
col_decoder_min_y_offset = self.col_decoder_inst[0].by()
|
self.min_x_offset = min([x.lx() for x in self.insts])
|
||||||
else:
|
|
||||||
col_decoder_min_y_offset = row_decoder_min_y_offset
|
|
||||||
|
|
||||||
if self.num_banks>1:
|
|
||||||
# The control gating logic is below the decoder
|
|
||||||
# Min of the control gating logic and write driver.
|
|
||||||
self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset)
|
|
||||||
else:
|
|
||||||
# Just the min of the decoder logic logic and write driver.
|
|
||||||
self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset)
|
|
||||||
|
|
||||||
# The max point is always the top of the precharge bitlines
|
|
||||||
# Add a vdd and gnd power rail above the array
|
|
||||||
# FIXME: Update multiport
|
|
||||||
self.max_y_offset = self.bitcell_array_inst.ur().y + 3*self.m1_width
|
|
||||||
self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width
|
|
||||||
self.min_x_offset = self.row_decoder_inst[0].lx()
|
|
||||||
|
|
||||||
# # Create the core bbox for the power rings
|
# # Create the core bbox for the power rings
|
||||||
ur = vector(self.max_x_offset, self.max_y_offset)
|
ur = vector(self.max_x_offset, self.max_y_offset)
|
||||||
|
|
@ -701,62 +827,76 @@ class bank(design.design):
|
||||||
|
|
||||||
# Overall central bus width. It includes all the column mux lines,
|
# Overall central bus width. It includes all the column mux lines,
|
||||||
# and control lines.
|
# and control lines.
|
||||||
|
|
||||||
|
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.
|
# 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
|
||||||
for port in self.all_ports:
|
|
||||||
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset)
|
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset)
|
||||||
control_bus_length = self.max_y_offset - self.min_y_offset
|
control_bus_length = self.max_y_offset - self.min_y_offset
|
||||||
self.bus_xoffset = self.create_bus(layer="metal2",
|
self.bus_xoffset[0] = self.create_bus(layer="metal2",
|
||||||
pitch=self.m2_pitch,
|
pitch=self.m2_pitch,
|
||||||
offset=control_bus_offset,
|
offset=control_bus_offset,
|
||||||
names=self.control_signals[port],
|
names=self.control_signals[0],
|
||||||
|
length=control_bus_length,
|
||||||
|
vertical=True,
|
||||||
|
make_pins=(self.num_banks==1))
|
||||||
|
|
||||||
|
# Port 1
|
||||||
|
if len(self.all_ports)==2:
|
||||||
|
control_bus_offset = vector(self.bitcell_array.width + self.m2_width, self.min_y_offset)
|
||||||
|
self.bus_xoffset[1] = self.create_bus(layer="metal2",
|
||||||
|
pitch=self.m2_pitch,
|
||||||
|
offset=control_bus_offset,
|
||||||
|
names=self.control_signals[1],
|
||||||
length=control_bus_length,
|
length=control_bus_length,
|
||||||
vertical=True,
|
vertical=True,
|
||||||
make_pins=(self.num_banks==1))
|
make_pins=(self.num_banks==1))
|
||||||
|
|
||||||
|
|
||||||
def route_precharge_to_bitcell_array(self):
|
def route_precharge_to_bitcell_array(self, port):
|
||||||
""" Routing of BL and BR between pre-charge and bitcell array """
|
""" Routing of BL and BR between pre-charge and bitcell array """
|
||||||
|
|
||||||
# FIXME: Update for multiport
|
inst2 = self.precharge_array_inst[port]
|
||||||
for port in self.read_ports:
|
inst1 = self.bitcell_array_inst
|
||||||
for col in range(self.num_cols):
|
inst1_bl_name = self.bl_names[port]+"_{}"
|
||||||
precharge_bl = self.precharge_array_inst[port].get_pin("bl_{}".format(col)).uc()
|
inst1_br_name = self.br_names[port]+"_{}"
|
||||||
precharge_br = self.precharge_array_inst[port].get_pin("br_{}".format(col)).uc()
|
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
|
||||||
bitcell_bl = self.bitcell_array_inst.get_pin(self.bl_names[port]+"_{}".format(col)).bc()
|
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
|
||||||
bitcell_br = self.bitcell_array_inst.get_pin(self.br_names[port]+"_{}".format(col)).bc()
|
|
||||||
|
|
||||||
yoffset = 0.5*(precharge_bl.y+bitcell_bl.y)
|
|
||||||
self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset),
|
|
||||||
vector(bitcell_bl.x,yoffset), bitcell_bl])
|
|
||||||
self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset),
|
|
||||||
vector(bitcell_br.x,yoffset), bitcell_br])
|
|
||||||
|
|
||||||
|
|
||||||
def route_col_mux_to_precharge_array(self):
|
|
||||||
|
def route_column_mux_to_precharge_array(self, port):
|
||||||
""" Routing of BL and BR between col mux and precharge array """
|
""" Routing of BL and BR between col mux and precharge array """
|
||||||
|
|
||||||
# Only do this if we have a column mux!
|
# Only do this if we have a column mux!
|
||||||
if self.col_addr_size==0:
|
if self.col_addr_size==0:
|
||||||
return
|
return
|
||||||
|
|
||||||
# FIXME: Update for multiport
|
bottom_inst = self.column_mux_array_inst[port]
|
||||||
for port in self.all_ports:
|
|
||||||
bottom_inst = self.col_mux_array_inst[port]
|
|
||||||
top_inst = self.precharge_array_inst[port]
|
top_inst = self.precharge_array_inst[port]
|
||||||
self.connect_bitlines(top_inst, bottom_inst, self.num_cols)
|
self.connect_bitlines(top_inst, bottom_inst, self.num_cols)
|
||||||
|
|
||||||
|
def route_column_mux_to_bitcell_array(self, port):
|
||||||
|
""" Routing of BL and BR between col mux bitcell array """
|
||||||
|
|
||||||
|
# Only do this if we have a column mux!
|
||||||
|
if self.col_addr_size==0:
|
||||||
|
return
|
||||||
|
|
||||||
|
bottom_inst = self.column_mux_array_inst[port]
|
||||||
|
top_inst = self.bitcell_array_inst
|
||||||
|
self.connect_bitlines(top_inst, bottom_inst, self.num_cols)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def route_sense_amp_to_col_mux_or_precharge_array(self):
|
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 """
|
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||||
|
|
||||||
for port in self.read_ports:
|
|
||||||
bottom_inst = self.sense_amp_array_inst[port]
|
bottom_inst = self.sense_amp_array_inst[port]
|
||||||
|
|
||||||
if self.col_addr_size>0:
|
if self.col_addr_size>0:
|
||||||
# Sense amp is connected to the col mux
|
# Sense amp is connected to the col mux
|
||||||
top_inst = self.col_mux_array_inst[port]
|
top_inst = self.column_mux_array_inst[port]
|
||||||
top_bl = "bl_out_{}"
|
top_bl = "bl_out_{}"
|
||||||
top_br = "br_out_{}"
|
top_br = "br_out_{}"
|
||||||
else:
|
else:
|
||||||
|
|
@ -765,24 +905,38 @@ class bank(design.design):
|
||||||
top_bl = "bl_{}"
|
top_bl = "bl_{}"
|
||||||
top_br = "br_{}"
|
top_br = "br_{}"
|
||||||
|
|
||||||
self.connect_bitlines(top_inst, bottom_inst, self.word_size,
|
self.connect_bitlines(inst1=top_inst, inst2=bottom_inst, num_bits=self.word_size,
|
||||||
top_bl_name=top_bl, top_br_name=top_br)
|
inst1_bl_name=top_bl, inst1_br_name=top_br)
|
||||||
|
|
||||||
def route_write_driver_to_sense_amp(self):
|
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 """
|
||||||
|
bottom_inst = self.write_driver_array_inst[port]
|
||||||
|
|
||||||
|
if self.col_addr_size>0:
|
||||||
|
# Sense amp is connected to the col mux
|
||||||
|
top_inst = self.column_mux_array_inst[port]
|
||||||
|
top_bl = "bl_out_{}"
|
||||||
|
top_br = "br_out_{}"
|
||||||
|
else:
|
||||||
|
# Sense amp is directly connected to the precharge array
|
||||||
|
top_inst = self.precharge_array_inst[port]
|
||||||
|
top_bl = "bl_{}"
|
||||||
|
top_br = "br_{}"
|
||||||
|
|
||||||
|
self.connect_bitlines(inst1=top_inst, inst2=bottom_inst, num_bits=self.word_size,
|
||||||
|
inst1_bl_name=top_bl, inst1_br_name=top_br)
|
||||||
|
|
||||||
|
def route_write_driver_to_sense_amp(self, port):
|
||||||
""" Routing of BL and BR between write driver and sense amp """
|
""" Routing of BL and BR between write driver and sense amp """
|
||||||
|
|
||||||
for port in self.write_ports:
|
inst1 = self.write_driver_array_inst[port]
|
||||||
bottom_inst = self.write_driver_array_inst[port]
|
inst2 = self.sense_amp_array_inst[port]
|
||||||
top_inst = self.sense_amp_array_inst[port]
|
self.connect_bitlines(inst1, inst2, self.word_size)
|
||||||
self.connect_bitlines(top_inst, bottom_inst, self.word_size)
|
|
||||||
|
|
||||||
|
|
||||||
|
def route_sense_amp_out(self, port):
|
||||||
def route_sense_amp_out(self):
|
|
||||||
""" Add pins for the sense amp output """
|
""" Add pins for the sense amp output """
|
||||||
|
|
||||||
# FIXME: Update for multiport
|
|
||||||
for port in self.read_ports:
|
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit))
|
data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit))
|
||||||
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_ports[port],bit),
|
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_ports[port],bit),
|
||||||
|
|
@ -792,12 +946,10 @@ class bank(design.design):
|
||||||
width=data_pin.width())
|
width=data_pin.width())
|
||||||
|
|
||||||
|
|
||||||
def route_row_decoder(self):
|
def route_row_decoder(self, port):
|
||||||
""" Routes the row decoder inputs and supplies """
|
""" Routes the row decoder inputs and supplies """
|
||||||
|
|
||||||
# FIXME: Update for multiport
|
|
||||||
# Create inputs for the row address lines
|
# Create inputs for the row address lines
|
||||||
for port in self.all_ports:
|
|
||||||
for row in range(self.row_addr_size):
|
for row in range(self.row_addr_size):
|
||||||
addr_idx = row + self.col_addr_size
|
addr_idx = row + self.col_addr_size
|
||||||
decoder_name = "addr_{}".format(row)
|
decoder_name = "addr_{}".format(row)
|
||||||
|
|
@ -805,22 +957,33 @@ class bank(design.design):
|
||||||
self.copy_layout_pin(self.row_decoder_inst[port], decoder_name, addr_name)
|
self.copy_layout_pin(self.row_decoder_inst[port], decoder_name, addr_name)
|
||||||
|
|
||||||
|
|
||||||
def route_write_driver(self):
|
def route_write_driver_in(self, port):
|
||||||
""" Connecting write driver """
|
""" Connecting write driver """
|
||||||
for port in self.all_ports:
|
|
||||||
for row in range(self.word_size):
|
for row in range(self.word_size):
|
||||||
data_name = "data_{}".format(row)
|
data_name = "data_{}".format(row)
|
||||||
din_name = "din{0}_{1}".format(port,row)
|
din_name = "din{0}_{1}".format(port,row)
|
||||||
self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name)
|
self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name)
|
||||||
|
|
||||||
def connect_bitlines(self, top_inst, bottom_inst, num_items,
|
def connect_bitlines(self, inst1, inst2, num_bits,
|
||||||
top_bl_name="bl_{}", top_br_name="br_{}", bottom_bl_name="bl_{}", bottom_br_name="br_{}"):
|
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
|
||||||
|
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
|
||||||
"""
|
"""
|
||||||
Connect the bl and br of two modules.
|
Connect the bl and br of two modules.
|
||||||
This assumes that they have sufficient space to create a jog
|
This assumes that they have sufficient space to create a jog
|
||||||
in the middle between the two modules (if needed)
|
in the middle between the two modules (if needed).
|
||||||
"""
|
"""
|
||||||
for col in range(num_items):
|
|
||||||
|
# determine top and bottom automatically.
|
||||||
|
# since they don't overlap, we can just check the bottom y coordinate.
|
||||||
|
if inst1.by() < inst2.by():
|
||||||
|
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name)
|
||||||
|
(top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name)
|
||||||
|
else:
|
||||||
|
(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)
|
||||||
|
|
||||||
|
for col in range(num_bits):
|
||||||
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc()
|
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc()
|
||||||
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc()
|
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc()
|
||||||
top_bl = top_inst.get_pin(top_bl_name.format(col)).bc()
|
top_bl = top_inst.get_pin(top_bl_name.format(col)).bc()
|
||||||
|
|
@ -833,9 +996,16 @@ class bank(design.design):
|
||||||
vector(top_br.x,yoffset), top_br])
|
vector(top_br.x,yoffset), top_br])
|
||||||
|
|
||||||
|
|
||||||
def route_wordline_driver(self):
|
def route_wordline_driver(self, port):
|
||||||
|
""" Connect Wordline driver to bitcell array wordline """
|
||||||
|
if port%2:
|
||||||
|
self.route_wordline_driver_right(port)
|
||||||
|
else:
|
||||||
|
self.route_wordline_driver_left(port)
|
||||||
|
|
||||||
|
def route_wordline_driver_left(self, port):
|
||||||
""" Connecting Wordline driver output to Bitcell WL connection """
|
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||||
for port in self.all_ports:
|
|
||||||
for row in range(self.num_rows):
|
for row in range(self.num_rows):
|
||||||
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
||||||
decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).rc()
|
decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).rc()
|
||||||
|
|
@ -852,19 +1022,42 @@ class bank(design.design):
|
||||||
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||||
|
|
||||||
|
|
||||||
def route_column_address_lines(self):
|
def route_wordline_driver_right(self, port):
|
||||||
|
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||||
|
|
||||||
|
for row in range(self.num_rows):
|
||||||
|
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
||||||
|
decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).lc()
|
||||||
|
driver_in_pos = self.wordline_driver_inst[port].get_pin("in_{}".format(row)).rc()
|
||||||
|
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0)
|
||||||
|
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1)
|
||||||
|
self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos])
|
||||||
|
|
||||||
|
# The mid guarantees we exit the input cell to the right.
|
||||||
|
driver_wl_pos = self.wordline_driver_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.5,1)+bitcell_wl_pos.scale(0.5,0)
|
||||||
|
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
|
||||||
|
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||||
|
|
||||||
|
def route_column_address_lines(self, port):
|
||||||
|
if port%2:
|
||||||
|
self.route_column_address_lines_right(port)
|
||||||
|
else:
|
||||||
|
self.route_column_address_lines_left(port)
|
||||||
|
|
||||||
|
def route_column_address_lines_left(self, port):
|
||||||
""" Connecting the select lines of column mux to the address bus """
|
""" Connecting the select lines of column mux to the address bus """
|
||||||
if not self.col_addr_size>0:
|
if not self.col_addr_size>0:
|
||||||
return
|
return
|
||||||
|
|
||||||
for port in self.all_ports:
|
|
||||||
if self.col_addr_size == 1:
|
if self.col_addr_size == 1:
|
||||||
|
|
||||||
# Connect to sel[0] and sel[1]
|
# Connect to sel[0] and sel[1]
|
||||||
decode_names = ["Zb", "Z"]
|
decode_names = ["Zb", "Z"]
|
||||||
|
|
||||||
# The Address LSB
|
# The Address LSB
|
||||||
self.copy_layout_pin(self.col_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:
|
elif self.col_addr_size > 1:
|
||||||
decode_names = []
|
decode_names = []
|
||||||
|
|
@ -874,17 +1067,51 @@ class bank(design.design):
|
||||||
for i in range(self.col_addr_size):
|
for i in range(self.col_addr_size):
|
||||||
decoder_name = "in_{}".format(i)
|
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.col_decoder_inst[port], decoder_name, addr_name)
|
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||||
|
|
||||||
offset = self.col_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
||||||
|
|
||||||
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
||||||
|
|
||||||
route_map = list(zip(decode_names, sel_names))
|
route_map = list(zip(decode_names, sel_names))
|
||||||
decode_pins = {key: self.col_decoder_inst[port].get_pin(key) for key in decode_names }
|
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
|
||||||
col_mux_pins = {key: self.col_mux_array_inst[port].get_pin(key) for key in sel_names }
|
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
|
||||||
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
||||||
all_pins = {**decode_pins, **col_mux_pins}
|
all_pins = {**decode_pins, **column_mux_pins}
|
||||||
|
self.create_vertical_channel_route(route_map, all_pins, offset)
|
||||||
|
|
||||||
|
def route_column_address_lines_right(self, port):
|
||||||
|
""" Connecting the select lines of column mux to the address bus """
|
||||||
|
if not self.col_addr_size>0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.col_addr_size == 1:
|
||||||
|
|
||||||
|
# Connect to sel[0] and sel[1]
|
||||||
|
decode_names = ["Zb", "Z"]
|
||||||
|
|
||||||
|
# The Address LSB
|
||||||
|
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
||||||
|
|
||||||
|
elif self.col_addr_size > 1:
|
||||||
|
decode_names = []
|
||||||
|
for i in range(self.num_col_addr_lines):
|
||||||
|
decode_names.append("out_{}".format(i))
|
||||||
|
|
||||||
|
for i in range(self.col_addr_size):
|
||||||
|
decoder_name = "in_{}".format(i)
|
||||||
|
addr_name = "addr{0}_{1}".format(port,i)
|
||||||
|
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||||
|
|
||||||
|
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
|
||||||
|
|
||||||
|
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
||||||
|
|
||||||
|
route_map = list(zip(decode_names, sel_names))
|
||||||
|
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
|
||||||
|
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
|
||||||
|
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
||||||
|
all_pins = {**decode_pins, **column_mux_pins}
|
||||||
self.create_vertical_channel_route(route_map, all_pins, offset)
|
self.create_vertical_channel_route(route_map, all_pins, offset)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -923,16 +1150,17 @@ class bank(design.design):
|
||||||
# offset=data_pin.center())
|
# offset=data_pin.center())
|
||||||
|
|
||||||
# Add labels on the decoder
|
# Add labels on the decoder
|
||||||
|
for port in self.write_ports:
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
data_name = "dec_out_{}".format(i)
|
data_name = "dec_out_{}".format(i)
|
||||||
pin_name = "in_{}".format(i)
|
pin_name = "in_{}".format(i)
|
||||||
data_pin = self.wordline_driver_inst[0].get_pin(pin_name)
|
data_pin = self.wordline_driver_inst[port].get_pin(pin_name)
|
||||||
self.add_label(text=data_name,
|
self.add_label(text=data_name,
|
||||||
layer="metal1",
|
layer="metal1",
|
||||||
offset=data_pin.center())
|
offset=data_pin.center())
|
||||||
|
|
||||||
|
|
||||||
def route_control_lines(self):
|
def route_control_lines(self, port):
|
||||||
""" Route the control lines of the entire bank """
|
""" Route the control lines of the entire bank """
|
||||||
|
|
||||||
# Make a list of tuples that we will connect.
|
# Make a list of tuples that we will connect.
|
||||||
|
|
@ -942,8 +1170,6 @@ class bank(design.design):
|
||||||
write_inst = 0
|
write_inst = 0
|
||||||
read_inst = 0
|
read_inst = 0
|
||||||
|
|
||||||
# Control lines for RW ports
|
|
||||||
for port in self.all_ports:
|
|
||||||
connection = []
|
connection = []
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc()))
|
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc()))
|
||||||
|
|
@ -955,7 +1181,7 @@ class bank(design.design):
|
||||||
connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[port].get_pin("en").lc()))
|
connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[port].get_pin("en").lc()))
|
||||||
|
|
||||||
for (control_signal, pin_pos) in connection:
|
for (control_signal, pin_pos) in connection:
|
||||||
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
|
control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y)
|
||||||
self.add_path("metal1", [control_pos, pin_pos])
|
self.add_path("metal1", [control_pos, pin_pos])
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
offset=control_pos,
|
offset=control_pos,
|
||||||
|
|
@ -965,12 +1191,11 @@ class bank(design.design):
|
||||||
control_signal = self.prefix+"clk_buf{}".format(port)
|
control_signal = self.prefix+"clk_buf{}".format(port)
|
||||||
pin_pos = self.wordline_driver_inst[port].get_pin("en").uc()
|
pin_pos = self.wordline_driver_inst[port].get_pin("en").uc()
|
||||||
mid_pos = pin_pos + vector(0,self.m1_pitch)
|
mid_pos = pin_pos + vector(0,self.m1_pitch)
|
||||||
control_x_offset = self.bus_xoffset[control_signal].x
|
control_x_offset = self.bus_xoffset[port][control_signal].x
|
||||||
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
|
control_pos = vector(control_x_offset, mid_pos.y)
|
||||||
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
|
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
|
||||||
control_via_pos = vector(control_x_offset, mid_pos.y)
|
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
offset=control_via_pos,
|
offset=control_pos,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,12 @@ class bank_select(design.design):
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.calculate_module_offsets()
|
self.calculate_module_offsets()
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route_modules()
|
self.route_instances()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ class bank_select(design.design):
|
||||||
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
|
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
|
||||||
self.width = self.xoffset_inv + self.inv4x.width
|
self.width = self.xoffset_inv + self.inv4x.width
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
|
|
||||||
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
||||||
mod=self.inv_sel)
|
mod=self.inv_sel)
|
||||||
|
|
@ -152,7 +152,7 @@ class bank_select(design.design):
|
||||||
"vdd",
|
"vdd",
|
||||||
"gnd"])
|
"gnd"])
|
||||||
|
|
||||||
def place_modules(self):
|
def place_instances(self):
|
||||||
|
|
||||||
# bank select inverter
|
# bank select inverter
|
||||||
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
||||||
|
|
@ -195,7 +195,7 @@ class bank_select(design.design):
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
def route_modules(self):
|
def route_instances(self):
|
||||||
|
|
||||||
# bank_sel is vertical wire
|
# bank_sel is vertical wire
|
||||||
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
|
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class bitcell_array(design.design):
|
||||||
""" Create and connect the netlist """
|
""" Create and connect the netlist """
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ class bitcell_array(design.design):
|
||||||
self.cell = self.mod_bitcell()
|
self.cell = self.mod_bitcell()
|
||||||
self.add_mod(self.cell)
|
self.add_mod(self.cell)
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
self.cell_inst = {}
|
self.cell_inst = {}
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,12 @@ class control_logic(design.design):
|
||||||
self.setup_signal_busses()
|
self.setup_signal_busses()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
""" Create layout and route between modules """
|
""" Create layout and route between modules """
|
||||||
self.route_rails()
|
self.route_rails()
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route_all()
|
self.route_all()
|
||||||
|
|
||||||
#self.add_lvs_correspondence_points()
|
#self.add_lvs_correspondence_points()
|
||||||
|
|
@ -197,8 +197,8 @@ class control_logic(design.design):
|
||||||
self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height)
|
self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height)
|
||||||
|
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
""" Create all the modules """
|
""" Create all the instances """
|
||||||
self.create_dffs()
|
self.create_dffs()
|
||||||
self.create_clk_row()
|
self.create_clk_row()
|
||||||
if (self.port_type == "rw") or (self.port_type == "w"):
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||||
|
|
@ -209,8 +209,8 @@ class control_logic(design.design):
|
||||||
self.create_rbl()
|
self.create_rbl()
|
||||||
|
|
||||||
|
|
||||||
def place_modules(self):
|
def place_instances(self):
|
||||||
""" Place all the modules """
|
""" Place all the instances """
|
||||||
# Keep track of all right-most instances to determine row boundary
|
# Keep track of all right-most instances to determine row boundary
|
||||||
# and add the vdd/gnd pins
|
# and add the vdd/gnd pins
|
||||||
self.row_end_inst = []
|
self.row_end_inst = []
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,13 @@ class dff_buf(design.design):
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.width = self.dff.width + self.inv1.width + self.inv2.width
|
self.width = self.dff.width + self.inv1.width + self.inv2.width
|
||||||
self.height = self.dff.height
|
self.height = self.dff.height
|
||||||
|
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route_wires()
|
self.route_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
@ -70,7 +70,7 @@ class dff_buf(design.design):
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
self.dff_inst=self.add_inst(name="dff_buf_dff",
|
self.dff_inst=self.add_inst(name="dff_buf_dff",
|
||||||
mod=self.dff)
|
mod=self.dff)
|
||||||
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
|
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
|
||||||
|
|
@ -83,7 +83,7 @@ class dff_buf(design.design):
|
||||||
mod=self.inv2)
|
mod=self.inv2)
|
||||||
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_modules(self):
|
def place_instances(self):
|
||||||
# Add the DFF
|
# Add the DFF
|
||||||
self.dff_inst.place(vector(0,0))
|
self.dff_inst.place(vector(0,0))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ class hierarchical_predecode(design.design):
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def create_modules(self):
|
def add_modules(self):
|
||||||
""" Create the INV and NAND gate """
|
""" Add the INV and NAND gate modules """
|
||||||
|
|
||||||
self.inv = pinv()
|
self.inv = pinv()
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.add_modules()
|
||||||
self.create_input_inverters()
|
self.create_input_inverters()
|
||||||
self.create_output_inverters()
|
self.create_output_inverters()
|
||||||
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"],
|
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"],
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.add_modules()
|
||||||
self.create_input_inverters()
|
self.create_input_inverters()
|
||||||
self.create_output_inverters()
|
self.create_output_inverters()
|
||||||
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],
|
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ class multibank(design.design):
|
||||||
|
|
||||||
self.compute_sizes()
|
self.compute_sizes()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
self.create_instances()
|
||||||
self.setup_layout_constraints()
|
self.setup_layout_constraints()
|
||||||
|
|
||||||
# FIXME: Move this to the add modules function
|
# FIXME: Move this to the add modules function
|
||||||
|
|
@ -111,7 +111,7 @@ class multibank(design.design):
|
||||||
|
|
||||||
self.route_supplies()
|
self.route_supplies()
|
||||||
|
|
||||||
def add_modules(self):
|
def create_instances(self):
|
||||||
""" Add modules. The order should not matter! """
|
""" Add modules. The order should not matter! """
|
||||||
|
|
||||||
# Above the bitcell array
|
# Above the bitcell array
|
||||||
|
|
@ -175,8 +175,8 @@ class multibank(design.design):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_modules(self):
|
def add_modules(self):
|
||||||
""" Create all the modules using the class loader """
|
""" Add all the modules using the class loader """
|
||||||
self.tri = self.mod_tri_gate()
|
self.tri = self.mod_tri_gate()
|
||||||
self.bitcell = self.mod_bitcell()
|
self.bitcell = self.mod_bitcell()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ class replica_bitline(design.design):
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.calculate_module_offsets()
|
self.calculate_module_offsets()
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route()
|
self.route()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ class replica_bitline(design.design):
|
||||||
self.access_tx = ptx(tx_type="pmos")
|
self.access_tx = ptx(tx_type="pmos")
|
||||||
self.add_mod(self.access_tx)
|
self.add_mod(self.access_tx)
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
""" Create all of the module instances in the logical netlist """
|
""" Create all of the module instances in the logical netlist """
|
||||||
|
|
||||||
# This is the threshold detect inverter on the output of the RBL
|
# This is the threshold detect inverter on the output of the RBL
|
||||||
|
|
@ -152,7 +152,7 @@ class replica_bitline(design.design):
|
||||||
self.wl_list = self.rbl.cell.list_all_wl_names()
|
self.wl_list = self.rbl.cell.list_all_wl_names()
|
||||||
self.bl_list = self.rbl.cell.list_all_bl_names()
|
self.bl_list = self.rbl.cell.list_all_bl_names()
|
||||||
|
|
||||||
def place_modules(self):
|
def place_instances(self):
|
||||||
""" Add all of the module instances in the logical netlist """
|
""" Add all of the module instances in the logical netlist """
|
||||||
|
|
||||||
# This is the threshold detect inverter on the output of the RBL
|
# This is the threshold detect inverter on the output of the RBL
|
||||||
|
|
|
||||||
|
|
@ -129,16 +129,15 @@ class wordline_driver(design.design):
|
||||||
nand2_xoffset = inv1_xoffset + self.inv.width
|
nand2_xoffset = inv1_xoffset + self.inv.width
|
||||||
inv2_xoffset = nand2_xoffset + self.nand2.width
|
inv2_xoffset = nand2_xoffset + self.nand2.width
|
||||||
|
|
||||||
self.width = inv2_xoffset + self.inv.height
|
self.width = inv2_xoffset + self.inv.width
|
||||||
driver_height = self.inv.height
|
|
||||||
self.height = self.inv.height * self.rows
|
self.height = self.inv.height * self.rows
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
if (row % 2):
|
if (row % 2):
|
||||||
y_offset = driver_height*(row + 1)
|
y_offset = self.inv.height*(row + 1)
|
||||||
inst_mirror = "MX"
|
inst_mirror = "MX"
|
||||||
else:
|
else:
|
||||||
y_offset = driver_height*row
|
y_offset = self.inv.height*row
|
||||||
inst_mirror = "R0"
|
inst_mirror = "R0"
|
||||||
|
|
||||||
inv1_offset = [inv1_xoffset, y_offset]
|
inv1_offset = [inv1_xoffset, y_offset]
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@ class options(optparse.Values):
|
||||||
debug_level = 0
|
debug_level = 0
|
||||||
# When enabled, layout is not generated (and no DRC or LVS are performed)
|
# When enabled, layout is not generated (and no DRC or LVS are performed)
|
||||||
netlist_only = False
|
netlist_only = False
|
||||||
# This determines whether LVS and DRC is checked for each submodule.
|
# This determines whether LVS and DRC is checked at all.
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
# This determines whether LVS and DRC is checked for every submodule.
|
||||||
|
inline_lvsdrc = False
|
||||||
# Variable to select the variant of spice
|
# Variable to select the variant of spice
|
||||||
spice_name = ""
|
spice_name = ""
|
||||||
# The spice executable being used which is derived from the user PATH.
|
# The spice executable being used which is derived from the user PATH.
|
||||||
|
|
|
||||||
|
|
@ -653,63 +653,32 @@ class router(router_tech):
|
||||||
debug.info(2,"Analyzing pin groups for {}.".format(pin_name))
|
debug.info(2,"Analyzing pin groups for {}.".format(pin_name))
|
||||||
|
|
||||||
pin_set = self.pins[pin_name]
|
pin_set = self.pins[pin_name]
|
||||||
local_debug = False
|
|
||||||
|
|
||||||
# Put each pin in an equivalence class of it's own
|
# Put each pin in an equivalence class of it's own
|
||||||
equiv_classes = [set([x]) for x in pin_set]
|
equiv_classes = [set([x]) for x in pin_set]
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"INITIAL\n",equiv_classes)
|
|
||||||
|
|
||||||
def compare_classes(class1, class2):
|
|
||||||
"""
|
|
||||||
Determine if two classes should be combined and if so return
|
|
||||||
the combined set. Otherwise, return None.
|
|
||||||
"""
|
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"CLASS1:\n",class1)
|
|
||||||
debug.info(0,"CLASS2:\n",class2)
|
|
||||||
# Compare each pin in each class,
|
|
||||||
# and if any overlap, return the combined the class
|
|
||||||
for p1 in class1:
|
|
||||||
for p2 in class2:
|
|
||||||
if p1.overlaps(p2):
|
|
||||||
combined_class = class1 | class2
|
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"COMBINE:",pformat(combined_class))
|
|
||||||
return combined_class
|
|
||||||
|
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"NO COMBINE")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def combine_classes(equiv_classes):
|
def combine_classes(equiv_classes):
|
||||||
""" Recursive function to combine classes. """
|
|
||||||
local_debug = False
|
|
||||||
|
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"\nRECURSE:\n",pformat(equiv_classes))
|
|
||||||
if len(equiv_classes)==1:
|
|
||||||
return(equiv_classes)
|
|
||||||
|
|
||||||
for class1 in equiv_classes:
|
for class1 in equiv_classes:
|
||||||
for class2 in equiv_classes:
|
for class2 in equiv_classes:
|
||||||
if class1 == class2:
|
if class1 == class2:
|
||||||
continue
|
continue
|
||||||
class3 = compare_classes(class1, class2)
|
# Compare each pin in each class,
|
||||||
if class3:
|
# and if any overlap, update equiv_classes to include the combined the class
|
||||||
new_classes = equiv_classes
|
for p1 in class1:
|
||||||
new_classes.remove(class1)
|
for p2 in class2:
|
||||||
new_classes.remove(class2)
|
if p1.overlaps(p2):
|
||||||
new_classes.append(class3)
|
combined_class = class1 | class2
|
||||||
return(combine_classes(new_classes))
|
equiv_classes.remove(class1)
|
||||||
else:
|
equiv_classes.remove(class2)
|
||||||
|
equiv_classes.append(combined_class)
|
||||||
|
return(equiv_classes)
|
||||||
return(equiv_classes)
|
return(equiv_classes)
|
||||||
|
|
||||||
reduced_classes = combine_classes(equiv_classes)
|
old_length = math.inf
|
||||||
if local_debug:
|
while (len(equiv_classes)<old_length):
|
||||||
debug.info(0,"FINAL ",reduced_classes)
|
old_length = len(equiv_classes)
|
||||||
self.pin_groups[pin_name] = [pin_group(name=pin_name, pin_set=x, router=self) for x in reduced_classes]
|
equiv_classes = combine_classes(equiv_classes)
|
||||||
|
|
||||||
|
self.pin_groups[pin_name] = [pin_group(name=pin_name, pin_set=x, router=self) for x in equiv_classes]
|
||||||
|
|
||||||
def convert_pins(self, pin_name):
|
def convert_pins(self, pin_name):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,14 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
self.bank_inst=self.create_bank(0)
|
self.bank_inst=self.create_bank(0)
|
||||||
|
|
||||||
self.control_logic_inst = self.create_control_logic()
|
self.control_logic_insts = self.create_control_logic()
|
||||||
|
|
||||||
self.row_addr_dff_inst = self.create_row_addr_dff()
|
self.row_addr_dff_insts = self.create_row_addr_dff()
|
||||||
|
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
self.col_addr_dff_inst = self.create_col_addr_dff()
|
self.col_addr_dff_insts = self.create_col_addr_dff()
|
||||||
|
|
||||||
self.data_dff_inst = self.create_data_dff()
|
self.data_dff_insts = self.create_data_dff()
|
||||||
|
|
||||||
def place_modules(self):
|
def place_modules(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -58,14 +58,14 @@ class sram_1bank(sram_base):
|
||||||
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
||||||
# up to the row address DFFs.
|
# up to the row address DFFs.
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
control_pos = vector(-self.control_logic.width - 2*self.m2_pitch,
|
control_pos = vector(-self.control_logic_rw.width - 2*self.m2_pitch,
|
||||||
self.bank.bank_center.y - self.control_logic.control_logic_center.y)
|
self.bank.bank_center.y - self.control_logic_rw.control_logic_center.y)
|
||||||
self.control_logic_inst[port].place(control_pos)
|
self.control_logic_insts[port].place(control_pos)
|
||||||
|
|
||||||
# The row address bits are placed above the control logic aligned on the right.
|
# The row address bits are placed above the control logic aligned on the right.
|
||||||
row_addr_pos = vector(self.control_logic_inst[0].rx() - self.row_addr_dff.width,
|
row_addr_pos = vector(self.control_logic_insts[0].rx() - self.row_addr_dff.width,
|
||||||
self.control_logic_inst[0].uy())
|
self.control_logic_insts[0].uy())
|
||||||
self.row_addr_dff_inst[port].place(row_addr_pos)
|
self.row_addr_dff_insts[port].place(row_addr_pos)
|
||||||
|
|
||||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||||
data_gap = -self.m2_pitch*(self.word_size+1)
|
data_gap = -self.m2_pitch*(self.word_size+1)
|
||||||
|
|
@ -75,7 +75,7 @@ class sram_1bank(sram_base):
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
|
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
|
||||||
data_gap - self.col_addr_dff.height)
|
data_gap - self.col_addr_dff.height)
|
||||||
self.col_addr_dff_inst[port].place(col_addr_pos)
|
self.col_addr_dff_insts[port].place(col_addr_pos)
|
||||||
|
|
||||||
# Add the data flops below the bank to the right of the center of bank:
|
# Add the data flops below the bank to the right of the center of bank:
|
||||||
# This relies on the center point of the bank:
|
# This relies on the center point of the bank:
|
||||||
|
|
@ -84,7 +84,7 @@ class sram_1bank(sram_base):
|
||||||
# sense amps.
|
# sense amps.
|
||||||
data_pos = vector(self.bank.bank_center.x,
|
data_pos = vector(self.bank.bank_center.x,
|
||||||
data_gap - self.data_dff.height)
|
data_gap - self.data_dff.height)
|
||||||
self.data_dff_inst[port].place(data_pos)
|
self.data_dff_insts[port].place(data_pos)
|
||||||
|
|
||||||
# two supply rails are already included in the bank, so just 2 here.
|
# two supply rails are already included in the bank, so just 2 here.
|
||||||
# self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
|
# self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
|
||||||
|
|
@ -97,7 +97,7 @@ class sram_1bank(sram_base):
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
# Connect the control pins as inputs
|
# Connect the control pins as inputs
|
||||||
for signal in self.control_logic_inputs[port] + ["clk"]:
|
for signal in self.control_logic_inputs[port] + ["clk"]:
|
||||||
self.copy_layout_pin(self.control_logic_inst[port], signal, signal+"{}".format(port))
|
self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port))
|
||||||
|
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
|
|
@ -105,14 +105,14 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# Lower address bits
|
# Lower address bits
|
||||||
for bit in range(self.col_addr_size):
|
for bit in range(self.col_addr_size):
|
||||||
self.copy_layout_pin(self.col_addr_dff_inst[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
|
# Upper address bits
|
||||||
for bit in range(self.row_addr_size):
|
for bit in range(self.row_addr_size):
|
||||||
self.copy_layout_pin(self.row_addr_dff_inst[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:
|
if port in self.write_ports:
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
self.copy_layout_pin(self.data_dff_inst[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))
|
||||||
|
|
||||||
def route(self):
|
def route(self):
|
||||||
""" Route a single bank SRAM """
|
""" Route a single bank SRAM """
|
||||||
|
|
@ -135,7 +135,7 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# This is the actual input to the SRAM
|
# This is the actual input to the SRAM
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.copy_layout_pin(self.control_logic_inst[port], "clk", "clk{}".format(port))
|
self.copy_layout_pin(self.control_logic_insts[port], "clk", "clk{}".format(port))
|
||||||
|
|
||||||
# Connect all of these clock pins to the clock in the central bus
|
# Connect all of these clock pins to the clock in the central bus
|
||||||
# This is something like a "spine" clock distribution. The two spines
|
# This is something like a "spine" clock distribution. The two spines
|
||||||
|
|
@ -147,23 +147,23 @@ class sram_1bank(sram_base):
|
||||||
bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center()
|
bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center()
|
||||||
|
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
dff_clk_pin = self.col_addr_dff_inst[port].get_pin("clk")
|
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
|
||||||
dff_clk_pos = dff_clk_pin.center()
|
dff_clk_pos = dff_clk_pin.center()
|
||||||
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
|
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
|
||||||
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
||||||
|
|
||||||
data_dff_clk_pin = self.data_dff_inst[port].get_pin("clk")
|
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
|
||||||
data_dff_clk_pos = data_dff_clk_pin.center()
|
data_dff_clk_pos = data_dff_clk_pin.center()
|
||||||
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
|
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
|
||||||
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
||||||
|
|
||||||
# This uses a metal2 track to the right of the control/row addr DFF
|
# This uses a metal2 track to the right of the control/row addr DFF
|
||||||
# to route vertically.
|
# to route vertically.
|
||||||
control_clk_buf_pin = self.control_logic_inst[port].get_pin("clk_buf")
|
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
|
||||||
control_clk_buf_pos = control_clk_buf_pin.rc()
|
control_clk_buf_pos = control_clk_buf_pin.rc()
|
||||||
row_addr_clk_pin = self.row_addr_dff_inst[port].get_pin("clk")
|
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
|
||||||
row_addr_clk_pos = row_addr_clk_pin.rc()
|
row_addr_clk_pos = row_addr_clk_pin.rc()
|
||||||
mid1_pos = vector(self.row_addr_dff_inst[port].rx() + self.m2_pitch,
|
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
|
||||||
row_addr_clk_pos.y)
|
row_addr_clk_pos.y)
|
||||||
mid2_pos = vector(mid1_pos.x,
|
mid2_pos = vector(mid1_pos.x,
|
||||||
control_clk_buf_pos.y)
|
control_clk_buf_pos.y)
|
||||||
|
|
@ -176,7 +176,7 @@ class sram_1bank(sram_base):
|
||||||
""" Route the outputs from the control logic module """
|
""" Route the outputs from the control logic module """
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
for signal in self.control_logic_outputs[port]:
|
for signal in self.control_logic_outputs[port]:
|
||||||
src_pin = self.control_logic_inst[port].get_pin(signal)
|
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_rail_from_left_m2m3(src_pin, dest_pin)
|
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
|
@ -190,7 +190,7 @@ class sram_1bank(sram_base):
|
||||||
for bit in range(self.row_addr_size):
|
for bit in range(self.row_addr_size):
|
||||||
flop_name = "dout_{}".format(bit)
|
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_inst[port].get_pin(flop_name)
|
flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name)
|
||||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||||
flop_pos = flop_pin.center()
|
flop_pos = flop_pin.center()
|
||||||
bank_pos = bank_pin.center()
|
bank_pos = bank_pin.center()
|
||||||
|
|
@ -206,13 +206,13 @@ class sram_1bank(sram_base):
|
||||||
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="metal1",
|
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
|
||||||
pitch=self.m1_pitch,
|
pitch=self.m1_pitch,
|
||||||
offset=self.col_addr_dff_inst[port].ul() + vector(0, self.m1_pitch),
|
offset=self.col_addr_dff_insts[port].ul() + vector(0, self.m1_pitch),
|
||||||
names=bus_names,
|
names=bus_names,
|
||||||
length=self.col_addr_dff_inst[port].width)
|
length=self.col_addr_dff_insts[port].width)
|
||||||
|
|
||||||
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
|
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
|
||||||
data_dff_map = zip(dff_names, bus_names)
|
data_dff_map = zip(dff_names, bus_names)
|
||||||
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst[port], col_addr_bus_offsets)
|
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)
|
data_bank_map = zip(bank_names, bus_names)
|
||||||
|
|
@ -223,13 +223,13 @@ class sram_1bank(sram_base):
|
||||||
""" Connect the output of the data flops to the write driver """
|
""" Connect the output of the data flops to the write driver """
|
||||||
# This is where the channel will start (y-dimension at least)
|
# This is where the channel will start (y-dimension at least)
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
offset = self.data_dff_inst[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_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
||||||
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)]
|
||||||
|
|
||||||
route_map = list(zip(bank_names, dff_names))
|
route_map = list(zip(bank_names, dff_names))
|
||||||
dff_pins = {key: self.data_dff_inst[port].get_pin(key) for key in dff_names }
|
dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_names }
|
||||||
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
|
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
|
||||||
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
||||||
all_pins = {**dff_pins, **bank_pins}
|
all_pins = {**dff_pins, **bank_pins}
|
||||||
|
|
@ -245,7 +245,7 @@ class sram_1bank(sram_base):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for n in self.control_logic_outputs[0]:
|
for n in self.control_logic_outputs[0]:
|
||||||
pin = self.control_logic_inst[0].get_pin(n)
|
pin = self.control_logic_insts[0].get_pin(n)
|
||||||
self.add_label(text=n,
|
self.add_label(text=n,
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
offset=pin.center())
|
offset=pin.center())
|
||||||
|
|
|
||||||
|
|
@ -216,6 +216,29 @@ class sram_base(design):
|
||||||
c = reload(__import__(OPTS.bitcell))
|
c = reload(__import__(OPTS.bitcell))
|
||||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||||
self.bitcell = self.mod_bitcell()
|
self.bitcell = self.mod_bitcell()
|
||||||
|
# <<<<<<< HEAD
|
||||||
|
# =======
|
||||||
|
|
||||||
|
# c = reload(__import__(OPTS.control_logic))
|
||||||
|
# self.mod_control_logic = getattr(c, OPTS.control_logic)
|
||||||
|
|
||||||
|
# # Create the control logic module for each port type
|
||||||
|
# if len(self.readwrite_ports)>0:
|
||||||
|
# self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
# words_per_row=self.words_per_row,
|
||||||
|
# port_type="rw")
|
||||||
|
# self.add_mod(self.control_logic_rw)
|
||||||
|
# if len(self.write_ports)>0:
|
||||||
|
# self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
# words_per_row=self.words_per_row,
|
||||||
|
# port_type="w")
|
||||||
|
# self.add_mod(self.control_logic_w)
|
||||||
|
# if len(self.read_ports)>0:
|
||||||
|
# self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
# words_per_row=self.words_per_row,
|
||||||
|
# port_type="r")
|
||||||
|
# self.add_mod(self.control_logic_r)
|
||||||
|
# >>>>>>> dev
|
||||||
|
|
||||||
# Create the address and control flops (but not the clk)
|
# Create the address and control flops (but not the clk)
|
||||||
from dff_array import dff_array
|
from dff_array import dff_array
|
||||||
|
|
@ -249,18 +272,27 @@ class sram_base(design):
|
||||||
#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
|
self.all_mods_except_control_done = True
|
||||||
|
|
||||||
#c = reload(__import__(OPTS.control_logic))
|
c = reload(__import__(OPTS.control_logic))
|
||||||
#self.mod_control_logic = getattr(c, OPTS.control_logic)
|
self.mod_control_logic = getattr(c, OPTS.control_logic)
|
||||||
from control_logic import control_logic
|
|
||||||
# Create the control logic module for each port type
|
# Create the control logic module for each port type
|
||||||
if OPTS.num_rw_ports>0:
|
if len(self.readwrite_ports)>0:
|
||||||
self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row,sram=self, port_type="rw")
|
self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
words_per_row=self.words_per_row,
|
||||||
|
sram=self,
|
||||||
|
port_type="rw")
|
||||||
self.add_mod(self.control_logic_rw)
|
self.add_mod(self.control_logic_rw)
|
||||||
if OPTS.num_w_ports>0:
|
if len(self.write_ports)>0:
|
||||||
self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row,sram=self, port_type="w")
|
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
words_per_row=self.words_per_row,
|
||||||
|
sram=self,
|
||||||
|
port_type="w")
|
||||||
self.add_mod(self.control_logic_w)
|
self.add_mod(self.control_logic_w)
|
||||||
if OPTS.num_r_ports>0:
|
if len(self.read_ports)>0:
|
||||||
self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row,sram=self, port_type="r")
|
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
words_per_row=self.words_per_row,
|
||||||
|
sram=self,
|
||||||
|
port_type="r")
|
||||||
self.add_mod(self.control_logic_r)
|
self.add_mod(self.control_logic_r)
|
||||||
|
|
||||||
def create_bank(self,bank_num):
|
def create_bank(self,bank_num):
|
||||||
|
|
@ -383,7 +415,8 @@ class sram_base(design):
|
||||||
|
|
||||||
|
|
||||||
def create_control_logic(self):
|
def create_control_logic(self):
|
||||||
""" Add and place control logic """
|
""" Add control logic instances """
|
||||||
|
|
||||||
insts = []
|
insts = []
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
if port in self.readwrite_ports:
|
if port in self.readwrite_ports:
|
||||||
|
|
@ -393,8 +426,7 @@ class sram_base(design):
|
||||||
else:
|
else:
|
||||||
mod = self.control_logic_r
|
mod = self.control_logic_r
|
||||||
|
|
||||||
insts.append(self.add_inst(name="control{}".format(port),
|
insts.append(self.add_inst(name="control{}".format(port), mod=mod))
|
||||||
mod=mod))
|
|
||||||
|
|
||||||
temp = ["csb{}".format(port)]
|
temp = ["csb{}".format(port)]
|
||||||
if port in self.readwrite_ports:
|
if port in self.readwrite_ports:
|
||||||
|
|
@ -456,24 +488,9 @@ class sram_base(design):
|
||||||
""" LH and HL are the same in analytical model. """
|
""" LH and HL are the same in analytical model. """
|
||||||
return self.bank.analytical_delay(vdd,slew,load)
|
return self.bank.analytical_delay(vdd,slew,load)
|
||||||
|
|
||||||
# def get_delay_to_wl(self):
|
|
||||||
# """Get the delay (in delay units) of the clk to a wordline in the bitcell array"""
|
|
||||||
# debug.check(self.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
|
|
||||||
# stage_efforts = self.determine_wordline_stage_efforts()
|
|
||||||
# clk_to_wl_delay = logical_effort.calculate_relative_delay(stage_efforts, self.pinv)
|
|
||||||
# debug.info(1, "Clock to wordline delay is {} delay units".format(clk_to_wl_delay))
|
|
||||||
# return clk_to_wl_delay
|
|
||||||
|
|
||||||
def determine_wordline_stage_efforts(self):
|
def determine_wordline_stage_efforts(self):
|
||||||
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
|
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
|
||||||
#clk
|
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
# clk_buf_cout = self.get_clk_cin()
|
|
||||||
# #Assume rw only. There are important differences with multiport that will need to be accounted for.
|
|
||||||
# if self.control_logic_rw != None:
|
|
||||||
# stage_effort_list += self.control_logic_rw.determine_wordline_stage_efforts(clk_buf_cout)
|
|
||||||
# else:
|
|
||||||
# stage_effort_list += self.control_logic_r.determine_wordline_stage_efforts(clk_buf_cout)
|
|
||||||
|
|
||||||
#Clk_buf originates from the control logic so only the bank is related to the wordline path
|
#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.
|
external_wordline_cout = 0 #No loading on the wordline other than in the bank.
|
||||||
|
|
@ -493,29 +510,6 @@ class sram_base(design):
|
||||||
|
|
||||||
return row_addr_clk_cin + data_clk_cin + col_addr_clk_cin + bank_clk_cin
|
return row_addr_clk_cin + data_clk_cin + col_addr_clk_cin + bank_clk_cin
|
||||||
|
|
||||||
# def get_delay_to_sen(self):
|
|
||||||
# """Get the delay (in delay units) of the clk to a sense amp enable.
|
|
||||||
# This does not incorporate the delay of the replica bitline.
|
|
||||||
# """
|
|
||||||
# debug.check(self.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
|
|
||||||
# stage_efforts = self.determine_sa_enable_stage_efforts()
|
|
||||||
# clk_to_sen_delay = logical_effort.calculate_relative_delay(stage_efforts, self.pinv)
|
|
||||||
# debug.info(1, "Clock to s_en delay is {} delay units".format(clk_to_sen_delay))
|
|
||||||
# return clk_to_sen_delay
|
|
||||||
|
|
||||||
# def determine_sa_enable_stage_efforts(self):
|
|
||||||
# """Get the all the stage efforts for each stage in the path from clk to a sense amp enable"""
|
|
||||||
# stage_effort_list = []
|
|
||||||
# clk_buf_bar_cout = self.get_clk_bar_cin()
|
|
||||||
# clk_sen_cout = self.get_sen_cin()
|
|
||||||
# #Assume rw only. There are important differences with multiport that will need to be accounted for.
|
|
||||||
# if self.control_logic_rw != None:
|
|
||||||
# stage_effort_list += self.control_logic_rw.determine_sa_enable_stage_efforts(clk_buf_bar_cout, clk_sen_cout)
|
|
||||||
# else:
|
|
||||||
# stage_effort_list += self.control_logic_r.determine_sa_enable_stage_efforts(clk_buf_bar_cout, clk_sen_cout)
|
|
||||||
|
|
||||||
# return stage_effort_list
|
|
||||||
|
|
||||||
def get_clk_bar_cin(self):
|
def get_clk_bar_cin(self):
|
||||||
"""Gets the capacitive load the of clock (clk_buf_bar) for the sram"""
|
"""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.
|
#As clk_buf_bar is an output of the control logic. The cap for that module is not determined here.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Run a regression test on 1rw 1r sram bank
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header,openram_test
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class single_bank_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
from bank import bank
|
||||||
|
|
||||||
|
OPTS.bitcell = "bitcell_1rw_1r"
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
|
||||||
|
from sram_config import sram_config
|
||||||
|
c = sram_config(word_size=4,
|
||||||
|
num_words=16)
|
||||||
|
|
||||||
|
c.words_per_row=1
|
||||||
|
debug.info(1, "No column mux")
|
||||||
|
a = bank(c, name="bank1_single")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
c.num_words=32
|
||||||
|
c.words_per_row=2
|
||||||
|
debug.info(1, "Two way column mux")
|
||||||
|
a = bank(c, name="bank2_single")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
c.num_words=64
|
||||||
|
c.words_per_row=4
|
||||||
|
debug.info(1, "Four way column mux")
|
||||||
|
a = bank(c, name="bank3_single")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
c.word_size=2
|
||||||
|
c.num_words=128
|
||||||
|
c.words_per_row=8
|
||||||
|
debug.info(1, "Eight way column mux")
|
||||||
|
a = bank(c, name="bank4_single")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
Loading…
Reference in New Issue