Fixed merge conflict, moved control logic mod instantiation, removed some commented out code.

This commit is contained in:
Hunter Nichols 2018-11-14 13:53:27 -08:00
commit e9f6566e59
18 changed files with 745 additions and 491 deletions

View File

@ -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)

View File

@ -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.")

View File

@ -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)

View File

@ -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")

View File

@ -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):

View File

@ -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 = []

View File

@ -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))

View File

@ -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)

View File

@ -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"],

View File

@ -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"],

View File

@ -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()

View File

@ -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

View File

@ -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]

View File

@ -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.

View File

@ -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):
""" """

View File

@ -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())

View File

@ -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.

View File

@ -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()