Merge branch 'dev' into bisr

This commit is contained in:
Aditi Sinha
2020-03-22 21:58:04 +00:00
34 changed files with 1369 additions and 1394 deletions
+177 -187
View File
@@ -17,9 +17,9 @@ from globals import OPTS
class bank(design.design):
"""
Dynamically generated a single bank including bitcell array,
hierarchical_decoder, precharge, (optional column_mux and column decoder),
hierarchical_decoder, precharge, (optional column_mux and column decoder),
write driver and sense amplifiers.
This can create up to two ports in any combination: rw, w, r.
This can create up to two ports in any combination: rw, w, r.
"""
def __init__(self, sram_config, name=""):
@@ -27,15 +27,15 @@ class bank(design.design):
self.sram_config = sram_config
sram_config.set_local_config(self)
if self.write_size:
self.num_wmasks = int(self.word_size/self.write_size)
self.num_wmasks = int(self.word_size / self.write_size)
else:
self.num_wmasks = 0
if name == "":
name = "bank_{0}_{1}".format(self.word_size, self.num_words)
design.design.__init__(self, name)
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words))
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
self.num_words))
# The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the input signals to create
@@ -47,18 +47,17 @@ class bank(design.design):
self.create_netlist()
if not OPTS.netlist_only:
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
debug.check(len(self.all_ports)<=2,
"Bank layout cannot handle more than two ports.")
self.create_layout()
self.add_boundary()
def create_netlist(self):
self.compute_sizes()
self.add_modules()
self.add_pins() # Must create the replica bitcell array first
self.create_instances()
def create_layout(self):
self.place_instances()
@@ -66,35 +65,34 @@ class bank(design.design):
self.route_layout()
# Can remove the following, but it helps for debug!
#self.add_lvs_correspondence_points()
# self.add_lvs_correspondence_points()
# Remember the bank center for further placement
self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1)
self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1)
self.bank_array_ur = self.bitcell_array_inst.ur()
self.bank_array_ul = self.bitcell_array_inst.ul()
self.DRC_LVS()
def add_pins(self):
""" Adding pins for Bank module"""
for port in self.read_ports:
for bit in range(self.word_size):
self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT")
self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT")
for port in self.all_ports:
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT")
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
for port in self.write_ports:
for bit in range(self.word_size):
self.add_pin("din{0}_{1}".format(port,bit),"INPUT")
self.add_pin("din{0}_{1}".format(port,bit), "INPUT")
for port in self.all_ports:
for bit in range(self.addr_size):
self.add_pin("addr{0}_{1}".format(port,bit),"INPUT")
self.add_pin("addr{0}_{1}".format(port,bit), "INPUT")
# For more than one bank, we have a bank select and name
# the signals gated_*.
if self.num_banks > 1:
for port in self.all_ports:
self.add_pin("bank_sel{}".format(port),"INPUT")
self.add_pin("bank_sel{}".format(port), "INPUT")
for port in self.read_ports:
self.add_pin("s_en{0}".format(port), "INPUT")
for port in self.all_ports:
@@ -102,12 +100,11 @@ class bank(design.design):
for port in self.write_ports:
self.add_pin("w_en{0}".format(port), "INPUT")
for bit in range(self.num_wmasks):
self.add_pin("bank_wmask{0}_{1}".format(port,bit),"INPUT")
self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT")
for port in self.all_ports:
self.add_pin("wl_en{0}".format(port), "INPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def route_layout(self):
""" Create routing amoung the modules """
@@ -124,18 +121,27 @@ class bank(design.design):
self.route_supplies()
def route_rbl(self,port):
def route_rbl(self, port):
""" Route the rbl_bl and rbl_wl """
bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
self.add_layout_pin(text="rbl_bl{0}".format(port),
layer=bl_pin.layer,
offset=bl_pin.ll(),
height=bl_pin.height(),
width=bl_pin.width())
# This will ensure the pin is only on the top or bottom edge
if port % 2:
via_offset = bl_pin.uc() + vector(0, self.m2_pitch)
left_right_offset = vector(self.max_x_offset, via_offset.y)
else:
via_offset = bl_pin.bc() - vector(0, self.m2_pitch)
left_right_offset = vector(self.min_x_offset, via_offset.y)
if bl_pin == "m1":
self.add_via_center(layers=self.m1_stack,
offset=via_offset)
self.add_via_center(layers=self.m2_stack,
offset=via_offset)
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
layer="m3",
start=left_right_offset,
end=via_offset)
def route_bitlines(self, port):
""" Route the bitlines depending on the port type rw, w, or r. """
@@ -146,7 +152,6 @@ class bank(design.design):
self.route_port_data_out(port)
self.route_port_data_to_bitcell_array(port)
def create_instances(self):
""" Create the instances of the netlist. """
@@ -161,19 +166,18 @@ class bank(design.design):
Compute the empty instance offsets for port0 and port1 (if needed)
"""
self.port_data_offsets = [None]*len(self.all_ports)
self.port_address_offsets = [None]*len(self.all_ports)
self.column_decoder_offsets = [None]*len(self.all_ports)
self.bank_select_offsets = [None]*len(self.all_ports)
self.port_data_offsets = [None] * len(self.all_ports)
self.port_address_offsets = [None] * len(self.all_ports)
self.column_decoder_offsets = [None] * len(self.all_ports)
self.bank_select_offsets = [None] * len(self.all_ports)
# The center point for these cells are the upper-right corner of
# the bitcell array.
# The port address decoder/driver logic is placed on the right and mirrored on Y-axis.
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
self.bitcell_array_top = self.bitcell_array.height
self.bitcell_array_right = self.bitcell_array.width
self.bitcell_array_top = self.bitcell_array.height
self.bitcell_array_right = self.bitcell_array.width
# These are the offsets of the main array (excluding dummy and replica rows/cols)
self.main_bitcell_array_top = self.bitcell_array.bitcell_array_inst.uy()
@@ -185,7 +189,6 @@ class bank(design.design):
self.compute_instance_port0_offsets()
if len(self.all_ports)==2:
self.compute_instance_port1_offsets()
def compute_instance_port0_offsets(self):
"""
@@ -196,29 +199,30 @@ class bank(design.design):
# UPPER RIGHT QUADRANT
# Bitcell array is placed at (0,0)
self.bitcell_array_offset = vector(0,0)
self.bitcell_array_offset = vector(0, 0)
# LOWER RIGHT QUADRANT
# Below the bitcell array
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0)
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0)
# UPPER LEFT QUADRANT
# To the left of the bitcell array
x_offset = self.m2_gap + self.port_address.width
self.port_address_offsets[port] = vector(-x_offset,self.main_bitcell_array_bottom)
x_offset = self.m2_gap + self.port_address.width
self.port_address_offsets[port] = vector(-x_offset,
self.main_bitcell_array_bottom)
# LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver
# Place the col decoder left aligned with wordline driver
# This is also placed so that it's supply rails do not align with the SRAM-level
# control logic to allow control signals to easily pass over in M3
# by placing 1/2 a cell pitch down
x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width
if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = 0.5*self.dff.height + self.column_decoder.height
y_offset = 0.5 * self.dff.height + self.column_decoder.height
else:
y_offset = 0
self.column_decoder_offsets[port] = vector(-x_offset,-y_offset)
self.column_decoder_offsets[port] = vector(-x_offset, -y_offset)
# Bank select gets placed below the column decoder (x_offset doesn't change)
if self.col_addr_size > 0:
@@ -227,7 +231,7 @@ class bank(design.design):
y_offset = self.port_address_offsets[port].y
if self.num_banks > 1:
y_offset += self.bank_select.height + drc("well_to_well")
self.bank_select_offsets[port] = vector(-x_offset,-y_offset)
self.bank_select_offsets[port] = vector(-x_offset, -y_offset)
def compute_instance_port1_offsets(self):
"""
@@ -246,18 +250,19 @@ class bank(design.design):
# LOWER RIGHT QUADRANT
# To the left of the bitcell array
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap
self.port_address_offsets[port] = vector(x_offset,self.main_bitcell_array_bottom)
self.port_address_offsets[port] = vector(x_offset,
self.main_bitcell_array_bottom)
# UPPER RIGHT QUADRANT
# Place the col decoder right aligned with wordline driver
# Place the col decoder right aligned with wordline driver
# Above the bitcell array with a well spacing
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width
if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.bitcell_array_top + 0.5*self.dff.height + self.column_decoder.height
y_offset = self.bitcell_array_top + 0.5 * self.dff.height + self.column_decoder.height
else:
y_offset = self.bitcell_array_top
self.column_decoder_offsets[port] = vector(x_offset,y_offset)
self.column_decoder_offsets[port] = vector(x_offset, y_offset)
# Bank select gets placed above the column decoder (x_offset doesn't change)
if self.col_addr_size > 0:
@@ -265,7 +270,7 @@ class bank(design.design):
self.port_data[port].column_mux_offset.y + self.port_data[port].column_mux_array.height)
else:
y_offset = self.port_address_offsets[port].y
self.bank_select_offsets[port] = vector(x_offset,y_offset)
self.bank_select_offsets[port] = vector(x_offset, y_offset)
def place_instances(self):
""" Place the instances. """
@@ -281,11 +286,10 @@ class bank(design.design):
self.place_column_decoder(self.column_decoder_offsets)
self.place_bank_select(self.bank_select_offsets)
def compute_sizes(self):
""" Computes the required sizes to create the bank """
self.num_cols = int(self.words_per_row*self.word_size)
self.num_cols = int(self.words_per_row * self.word_size)
self.num_rows_temp = int(self.num_words / self.words_per_row)
self.num_rows = self.num_rows_temp + self.num_spare_rows
@@ -293,8 +297,10 @@ class bank(design.design):
self.col_addr_size = int(log(self.words_per_row, 2))
self.addr_size = self.col_addr_size + self.row_addr_size
debug.check(self.num_rows_temp*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.")
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.")
debug.check(self.num_rows_temp * self.num_cols==self.word_size * self.num_words,
"Invalid bank sizes.")
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,
"Invalid address break down.")
# The order of the control signals on the control bus:
self.input_control_signals = []
@@ -313,13 +319,13 @@ class bank(design.design):
self.num_control_lines = [len(x) for x in self.input_control_signals]
# The width of this bus is needed to place other modules (e.g. decoder) for each port
self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines]
self.central_bus_width = [self.m2_pitch * x + self.m2_width for x in self.num_control_lines]
# These will be outputs of the gaters if this is multibank, if not, normal signals.
self.control_signals = []
for port in self.all_ports:
if self.num_banks > 1:
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
else:
self.control_signals.append(self.input_control_signals[port])
@@ -327,19 +333,18 @@ class bank(design.design):
if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size
else:
self.num_col_addr_lines = 0
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
self.num_col_addr_lines = 0
self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
# A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3 * self.m2_pitch)
def add_modules(self):
""" Add all the modules using the class loader """
# create arrays of bitline and bitline_bar names for read, write, or all ports
self.bitcell = factory.create(module_type="bitcell")
self.bitcell = factory.create(module_type="bitcell")
self.bl_names = self.bitcell.get_all_bl_names()
self.br_names = self.bitcell.get_all_br_names()
self.wl_names = self.bitcell.get_all_wl_names()
@@ -353,13 +358,11 @@ class bank(design.design):
self.port_data.append(temp_pre)
self.add_mod(self.port_data[port])
self.port_address = factory.create(module_type="port_address",
cols=self.num_cols,
rows=self.num_rows)
self.add_mod(self.port_address)
self.port_rbl_map = self.all_ports
self.num_rbl = len(self.all_ports)
@@ -371,22 +374,20 @@ class bank(design.design):
bitcell_ports=self.all_ports)
self.add_mod(self.bitcell_array)
if(self.num_banks > 1):
self.bank_select = factory.create(module_type="bank_select")
self.add_mod(self.bank_select)
def create_bitcell_array(self):
""" Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
mod=self.bitcell_array)
temp = []
for col in range(self.num_cols):
for bitline in self.bitline_names:
temp.append("{0}_{1}".format(bitline,col))
temp.append("{0}_{1}".format(bitline, col))
for rbl in range(self.num_rbl):
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl)
temp.append(rbl_bl_name)
@@ -394,23 +395,21 @@ class bank(design.design):
temp.append(rbl_br_name)
for row in range(self.num_rows):
for wordline in self.wl_names:
temp.append("{0}_{1}".format(wordline,row))
temp.append("{0}_{1}".format(wordline, row))
for port in self.all_ports:
temp.append("wl_en{0}".format(port))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place_bitcell_array(self, offset):
""" Placing Bitcell Array """
self.bitcell_array_inst.place(offset)
def create_port_data(self):
""" Creating Port Data """
self.port_data_inst = [None]*len(self.all_ports)
self.port_data_inst = [None] * len(self.all_ports)
for port in self.all_ports:
self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port),
mod=self.port_data[port])
@@ -420,17 +419,17 @@ class bank(design.design):
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
temp.append(rbl_bl_name)
temp.append(rbl_br_name)
for col in range(self.num_cols):
temp.append("{0}_{1}".format(self.bl_names[port],col))
temp.append("{0}_{1}".format(self.br_names[port],col))
for col in range(self.num_cols):
temp.append("{0}_{1}".format(self.bl_names[port], col))
temp.append("{0}_{1}".format(self.br_names[port], col))
if port in self.read_ports:
for bit in range(self.word_size):
temp.append("dout{0}_{1}".format(port,bit))
temp.append("dout{0}_{1}".format(port, bit))
if port in self.write_ports:
for bit in range(self.word_size):
temp.append("din{0}_{1}".format(port,bit))
temp.append("din{0}_{1}".format(port, bit))
# Will be empty if no col addr lines
sel_names = ["sel{0}_{1}".format(port,x) for x in range(self.num_col_addr_lines)]
sel_names = ["sel{0}_{1}".format(port, x) for x in range(self.num_col_addr_lines)]
temp.extend(sel_names)
if port in self.read_ports:
temp.append("s_en{0}".format(port))
@@ -439,17 +438,16 @@ class bank(design.design):
temp.append("w_en{0}".format(port))
for bit in range(self.num_wmasks):
temp.append("bank_wmask{0}_{1}".format(port, bit))
temp.extend(["vdd","gnd"])
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_port_data(self, offsets):
""" Placing Port Data """
for port in self.all_ports:
# Top one is unflipped, bottom is flipped along X direction
if port%2 == 1:
if port % 2 == 1:
mirror = "R0"
else:
mirror = "MX"
@@ -458,42 +456,40 @@ class bank(design.design):
def create_port_address(self):
""" Create the hierarchical row decoder """
self.port_address_inst = [None]*len(self.all_ports)
self.port_address_inst = [None] * len(self.all_ports)
for port in self.all_ports:
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
mod=self.port_address)
temp = []
for bit in range(self.row_addr_size):
temp.append("addr{0}_{1}".format(port,bit+self.col_addr_size))
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
temp.append("wl_en{0}".format(port))
for row in range(self.num_rows):
temp.append("{0}_{1}".format(self.wl_names[port],row))
temp.append("{0}_{1}".format(self.wl_names[port], row))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_port_address(self, offsets):
""" Place the hierarchical row decoder """
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
# The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs.
# The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs.
# The wires are actually routed after we placed the stuff on both sides.
# The predecoder is below the x-axis and the main decoder is above the x-axis
# The address flop and decoder are aligned in the x coord.
for port in self.all_ports:
if port%2:
if port % 2:
mirror = "MY"
else:
mirror = "R0"
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
def create_column_decoder(self):
"""
"""
Create a 2:4 or 3:8 column address decoder.
"""
@@ -511,40 +507,38 @@ class bank(design.design):
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height)
else:
# No error checking before?
debug.error("Invalid column decoder?",-1)
debug.error("Invalid column decoder?", -1)
self.add_mod(self.column_decoder)
self.column_decoder_inst = [None]*len(self.all_ports)
self.column_decoder_inst = [None] * len(self.all_ports)
for port in self.all_ports:
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
mod=self.column_decoder)
temp = []
for bit in range(self.col_addr_size):
temp.append("addr{0}_{1}".format(port,bit))
temp.append("addr{0}_{1}".format(port, bit))
for bit in range(self.num_col_addr_lines):
temp.append("sel{0}_{1}".format(port,bit))
temp.append("sel{0}_{1}".format(port, bit))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_column_decoder(self, offsets):
"""
"""
Place a 2:4 or 3:8 column address decoder.
"""
if self.col_addr_size == 0:
return
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.")
debug.check(len(offsets)>=len(self.all_ports),
"Insufficient offsets to place column decoder.")
for port in self.all_ports:
if port%2 == 1:
if port % 2 == 1:
mirror = "XY"
else:
mirror = "R0"
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
def create_bank_select(self):
""" Create the bank select logic. """
@@ -552,7 +546,7 @@ class bank(design.design):
if not self.num_banks > 1:
return
self.bank_select_inst = [None]*len(self.all_ports)
self.bank_select_inst = [None] * len(self.all_ports)
for port in self.all_ports:
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
mod=self.bank_select)
@@ -564,24 +558,23 @@ class bank(design.design):
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_bank_select(self, offsets):
""" Place the bank select logic. """
if not self.num_banks > 1:
return
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place bank select logic.")
debug.check(len(offsets)>=len(self.all_ports),
"Insufficient offsets to place bank select logic.")
for port in self.all_ports:
self.bank_select_inst[port].place(offsets[port])
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts:
self.copy_power_pins(inst,"vdd")
self.copy_power_pins(inst,"gnd")
self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst, "gnd")
def route_bank_select(self, port):
""" Route the bank select logic. """
@@ -596,7 +589,7 @@ class bank(design.design):
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)]
copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)]
for signal in range(len(copy_control_signals)):
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
@@ -605,25 +598,24 @@ class bank(design.design):
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
name = self.control_signals[port][signal]
bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y)
self.add_path("m3",[out_pos, bus_pos])
self.add_path("m3", [out_pos, bus_pos])
self.add_via_center(layers=self.m2_stack,
offset=bus_pos)
self.add_via_center(layers=self.m1_stack,
offset=out_pos)
self.add_via_center(layers=self.m2_stack,
offset=out_pos)
def setup_routing_constraints(self):
"""
"""
After the modules are instantiated, find the dimensions for the
control bus, power ring, etc.
control bus, power ring, etc.
"""
self.max_y_offset = max([x.uy() for x in self.insts]) + 3*self.m1_width
self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width
self.min_y_offset = min([x.by() for x in self.insts])
self.max_x_offset = max([x.rx() for x in self.insts]) + 3*self.m1_width
self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width
self.min_x_offset = min([x.lx() for x in self.insts])
# # Create the core bbox for the power rings
@@ -633,7 +625,6 @@ class bank(design.design):
self.height = ur.y - ll.y
self.width = ur.x - ll.x
def route_central_bus(self):
""" Create the address, supply, and control signal central bus lines. """
@@ -641,13 +632,13 @@ class bank(design.design):
# Overall central bus width. It includes all the column mux lines,
# and control lines.
self.bus_xoffset = [None]*len(self.all_ports)
self.bus_xoffset = [None] * len(self.all_ports)
# Port 0
# The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset)
# The control bus is routed up to two pitches below the bitcell array
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2*self.m1_pitch
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
self.bus_xoffset[0] = self.create_bus(layer="m2",
pitch=self.m2_pitch,
offset=control_bus_offset,
@@ -659,7 +650,7 @@ class bank(design.design):
# Port 1
if len(self.all_ports)==2:
# The other control bus is routed up to two pitches above the bitcell array
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2*self.m1_pitch
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch,
self.max_y_offset - control_bus_length)
# The bus for the right port is reversed so that the rbl_wl is closest to the array
@@ -670,7 +661,6 @@ class bank(design.design):
length=control_bus_length,
vertical=True,
make_pins=(self.num_banks==1))
def route_port_data_to_bitcell_array(self, port):
""" Routing of BL and BR between port data and bitcell array """
@@ -678,15 +668,19 @@ class bank(design.design):
# Connect the regular bitlines
inst2 = self.port_data_inst[port]
inst1 = self.bitcell_array_inst
inst1_bl_name = self.bl_names[port]+"_{}"
inst1_br_name = self.br_names[port]+"_{}"
inst1_bl_name = self.bl_names[port] + "_{}"
inst1_br_name = self.br_names[port] + "_{}"
inst2_bl_name = inst2.mod.get_bl_names()+"_{}"
inst2_br_name = inst2.mod.get_br_names()+"_{}"
inst2_bl_name = inst2.mod.get_bl_names() + "_{}"
inst2_br_name = inst2.mod.get_br_names() + "_{}"
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name,
inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name)
self.connect_bitlines(inst1=inst1,
inst2=inst2,
num_bits=self.num_cols,
inst1_bl_name=inst1_bl_name,
inst1_br_name=inst1_br_name,
inst2_bl_name=inst2_bl_name,
inst2_br_name=inst2_br_name)
# Connect the replica bitlines
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
@@ -694,20 +688,17 @@ class bank(design.design):
self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl")
self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br")
def route_port_data_out(self, port):
""" Add pins for the port data out """
for bit in range(self.word_size):
data_pin = self.port_data_inst[port].get_pin("dout_{0}".format(bit))
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port,bit),
layer=data_pin.layer,
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port, bit),
layer=data_pin.layer,
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width())
def route_port_address_in(self, port):
""" Routes the row decoder inputs and supplies """
@@ -715,17 +706,15 @@ class bank(design.design):
for row in range(self.row_addr_size):
addr_idx = row + self.col_addr_size
decoder_name = "addr_{}".format(row)
addr_name = "addr{0}_{1}".format(port,addr_idx)
addr_name = "addr{0}_{1}".format(port, addr_idx)
self.copy_layout_pin(self.port_address_inst[port], decoder_name, addr_name)
def route_port_data_in(self, port):
""" Connecting port data in """
for row in range(self.word_size):
data_name = "din_{}".format(row)
din_name = "din{0}_{1}".format(port,row)
din_name = "din{0}_{1}".format(port, row)
self.copy_layout_pin(self.port_data_inst[port], data_name, din_name)
if self.word_size:
@@ -733,8 +722,6 @@ class bank(design.design):
wmask_name = "bank_wmask_{}".format(row)
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
@@ -752,19 +739,18 @@ class bank(design.design):
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name)
# Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
offset = bottom_inst.ul() + vector(0, self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
"""
Connect two pins of two modules.
Connect two pins of two modules.
This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed).
"""
@@ -785,11 +771,13 @@ class bank(design.design):
bottom_loc = bottom_pin.uc()
top_loc = top_pin.bc()
yoffset = 0.5*(top_loc.y+bottom_loc.y)
self.add_path(top_pin.layer,[bottom_loc, vector(bottom_loc.x,yoffset),
vector(top_loc.x,yoffset), top_loc])
yoffset = 0.5 * (top_loc.y + bottom_loc.y)
self.add_path(top_pin.layer,
[bottom_loc,
vector(bottom_loc.x, yoffset),
vector(top_loc.x, yoffset),
top_loc])
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name, inst1_br_name,
inst2_bl_name, inst2_br_name):
@@ -800,14 +788,13 @@ class bank(design.design):
for col in range(num_bits):
self.connect_bitline(inst1, inst2, inst1_bl_name.format(col), inst2_bl_name.format(col))
self.connect_bitline(inst1, inst2, inst1_br_name.format(col), inst2_br_name.format(col))
def route_port_address(self, port):
""" Connect Wordline driver to bitcell array wordline """
self.route_port_address_in(port)
if port%2:
if port % 2:
self.route_port_address_right(port)
else:
self.route_port_address_left(port)
@@ -818,21 +805,20 @@ class bank(design.design):
for row in range(self.num_rows):
# The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).rc()
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc()
mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].rx() + 0.5*self.bitcell_array_inst.lx(),0)
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0.5,1)
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).lc()
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1)
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_port_address_right(self, port):
""" Connecting Wordline driver output to Bitcell WL connection """
for row in range(self.num_rows):
# The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).lc()
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).rc()
mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].lx() + 0.5*self.bitcell_array_inst.rx(),0)
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0,1)
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).rc()
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_column_address_lines(self, port):
@@ -846,7 +832,7 @@ class bank(design.design):
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
@@ -855,11 +841,11 @@ class bank(design.design):
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
addr_name = "addr{0}_{1}".format(port, i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
if port%2:
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
if port % 2:
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_pitch, 0)
else:
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
@@ -872,7 +858,8 @@ class bank(design.design):
self.create_vertical_channel_route(route_map, offset, self.m1_stack)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
"""
This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
@@ -881,7 +868,7 @@ class bank(design.design):
wl_name = "wl_{}".format(i)
wl_pin = self.bitcell_array_inst.get_pin(wl_name)
self.add_label(text=wl_name,
layer="m1",
layer="m1",
offset=wl_pin.center())
# Add the bitline names
@@ -891,35 +878,35 @@ class bank(design.design):
bl_pin = self.bitcell_array_inst.get_pin(bl_name)
br_pin = self.bitcell_array_inst.get_pin(br_name)
self.add_label(text=bl_name,
layer="m2",
layer="m2",
offset=bl_pin.center())
self.add_label(text=br_name,
layer="m2",
layer="m2",
offset=br_pin.center())
# # Add the data output names to the sense amp output
# # Add the data output names to the sense amp output
# for i in range(self.word_size):
# data_name = "data_{}".format(i)
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
# self.add_label(text="sa_out_{}".format(i),
# layer="m2",
# layer="m2",
# offset=data_pin.center())
# Add labels on the decoder
for port in self.write_ports:
for i in range(self.word_size):
data_name = "dec_out_{}".format(i)
pin_name = "in_{}".format(i)
pin_name = "in_{}".format(i)
data_pin = self.wordline_driver_inst[port].get_pin(pin_name)
self.add_label(text=data_name,
layer="m1",
layer="m1",
offset=data_pin.center())
def route_control_lines(self, port):
""" Route the control lines of the entire bank """
# Make a list of tuples that we will connect.
# From control signal to the module pin
# From control signal to the module pin
# Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3
connection = []
@@ -944,58 +931,59 @@ class bank(design.design):
for (control_signal, pin_pos) in connection:
control_mid_pos = self.bus_xoffset[port][control_signal]
control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y)
control_pos = vector(self.bus_xoffset[port][control_signal].x, pin_pos.y)
self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=control_pos)
# clk to wordline_driver
control_signal = self.prefix+"wl_en{}".format(port)
if port%2:
control_signal = self.prefix + "wl_en{}".format(port)
if port % 2:
pin_pos = self.port_address_inst[port].get_pin("wl_en").uc()
mid_pos = pin_pos + vector(0,2*self.m2_gap) # to route down to the top of the bus
mid_pos = pin_pos + vector(0, 2 * self.m2_gap) # to route down to the top of the bus
else:
pin_pos = self.port_address_inst[port].get_pin("wl_en").bc()
mid_pos = pin_pos - vector(0,2*self.m2_gap) # to route down to the top of the bus
mid_pos = pin_pos - vector(0, 2 * self.m2_gap) # to route down to the top of the bus
control_x_offset = self.bus_xoffset[port][control_signal].x
control_pos = vector(control_x_offset, mid_pos.y)
self.add_wire(self.m1_stack,[pin_pos, mid_pos, control_pos])
self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos])
self.add_via_center(layers=self.m1_stack,
offset=control_pos)
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption
# Decoder is assumed to have settled before the negative edge of the clock.
# Delay model relies on this assumption
stage_effort_list = []
wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout
stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,inp_is_rise)
stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,
inp_is_rise)
return stage_effort_list
def get_wl_en_cin(self):
"""Get the relative capacitance of all the clk connections in the bank"""
#wl_en only used in the wordline driver.
# wl_en only used in the wordline driver.
return self.port_address.wordline_driver.get_wl_en_cin()
def get_w_en_cin(self):
"""Get the relative capacitance of all the clk connections in the bank"""
#wl_en only used in the wordline driver.
port = self.write_ports[0]
# wl_en only used in the wordline driver.
port = self.write_ports[0]
return self.port_data[port].write_driver.get_w_en_cin()
def get_clk_bar_cin(self):
"""Get the relative capacitance of all the clk_bar connections in the bank"""
#Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array.
# Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array.
#Precharges are the all the same in Mulitport, one is picked
# Precharges are the all the same in Mulitport, one is picked
port = self.read_ports[0]
return self.port_data[port].precharge_array.get_en_cin()
def get_sen_cin(self):
"""Get the relative capacitance of all the sense amp enable connections in the bank"""
#Current bank only uses sen as an enable for the sense amps.
port = self.read_ports[0]
# Current bank only uses sen as an enable for the sense amps.
port = self.read_ports[0]
return self.port_data[port].sense_amp_array.get_en_cin()
def graph_exclude_precharge(self):
@@ -1006,5 +994,7 @@ class bank(design.design):
def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell."""
return self.bitcell_array_inst.mod.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col)
return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name,
row,
col)
+9 -2
View File
@@ -101,13 +101,20 @@ class bitcell_base_array(design.design):
width=self.width,
height=wl_pin.height())
# For every second row and column, add a via for gnd and vdd
# For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space
# Default uses prefered directions for each layer; this cell property is only currently used by s8 tech (03/20)
try:
force_power_pins_vertical = cell_properties.bitcell_force_power_pins_vertical
except AttributeError:
force_power_pins_vertical = None
# Add vdd/gnd via stacks
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row,col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=force_power_pins_vertical, start_layer=pin.layer)
def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset
+104 -193
View File
@@ -20,8 +20,7 @@ class hierarchical_decoder(design.design):
def __init__(self, name, rows):
design.design.__init__(self, name)
self.NAND_FORMAT = "DEC_NAND_{0}"
self.INV_FORMAT = "DEC_INV_{0}"
self.AND_FORMAT = "DEC_AND_{0}"
self.pre2x4_inst = []
self.pre3x8_inst = []
@@ -58,12 +57,12 @@ class hierarchical_decoder(design.design):
self.inv = factory.create(module_type="pinv",
height=self.cell_height)
self.add_mod(self.inv)
self.nand2 = factory.create(module_type="pnand2",
self.and2 = factory.create(module_type="pand2",
height=self.cell_height)
self.add_mod(self.and2)
self.and3 = factory.create(module_type="pand3",
height=self.cell_height)
self.add_mod(self.nand2)
self.nand3 = factory.create(module_type="pnand3",
height=self.cell_height)
self.add_mod(self.nand3)
self.add_mod(self.and3)
self.add_decoders()
@@ -77,27 +76,27 @@ class hierarchical_decoder(design.design):
height=self.cell_height)
self.add_mod(self.pre3_8)
def determine_predecodes(self,num_inputs):
def determine_predecodes(self, num_inputs):
""" Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
needed based on the number of inputs """
if (num_inputs == 2):
return (1,0)
return (1, 0)
elif (num_inputs == 3):
return(0,1)
return(0, 1)
elif (num_inputs == 4):
return(2,0)
return(2, 0)
elif (num_inputs == 5):
return(1,1)
return(1, 1)
elif (num_inputs == 6):
return(3,0)
return(3, 0)
elif (num_inputs == 7):
return(2,1)
return(2, 1)
elif (num_inputs == 8):
return(1,2)
return(1, 2)
elif (num_inputs == 9):
return(0,3)
return(0, 3)
else:
debug.error("Invalid number of inputs for hierarchical decoder",-1)
debug.error("Invalid number of inputs for hierarchical decoder", -1)
def setup_netlist_constants(self):
self.predec_groups = [] # This array is a 2D array.
@@ -122,35 +121,35 @@ class hierarchical_decoder(design.design):
index = index + 1
self.predec_groups.append(lines)
def setup_layout_constants(self):
""" Calculate the overall dimensions of the hierarchical decoder """
# If we have 4 or fewer rows, the predecoder is the decoder itself
if self.num_inputs>=4:
self.total_number_of_predecoder_outputs = 4*self.no_of_pre2x4 + 8*self.no_of_pre3x8
self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8
else:
self.total_number_of_predecoder_outputs = 0
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),-1)
self.total_number_of_predecoder_outputs = 0
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),
-1)
# Calculates height and width of pre-decoder,
if self.no_of_pre3x8 > 0:
self.predecoder_width = self.pre3_8.width
self.predecoder_width = self.pre3_8.width
else:
self.predecoder_width = self.pre2_4.width
self.predecoder_height = self.pre2_4.height*self.no_of_pre2x4 + self.pre3_8.height*self.no_of_pre3x8
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
# Calculates height and width of row-decoder
# Calculates height and width of row-decoder
if (self.num_inputs == 4 or self.num_inputs == 5):
nand_width = self.nand2.width
nand_width = self.and2.width
else:
nand_width = self.nand3.width
self.internal_routing_width = self.m2_pitch*self.total_number_of_predecoder_outputs
nand_width = self.and3.width
self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs
self.row_decoder_height = self.inv.height * self.rows
self.input_routing_width = (self.num_inputs+1) * self.m2_pitch
# Calculates height and width of hierarchical decoder
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
# Calculates height and width of hierarchical decoder
self.height = self.row_decoder_height
self.width = self.input_routing_width + self.predecoder_width \
+ self.internal_routing_width + nand_width + self.inv.width
@@ -158,7 +157,7 @@ class hierarchical_decoder(design.design):
def route_input_rails(self):
""" Create input rails for the predecoders """
# inputs should be as high as the decoders
input_height = self.no_of_pre2x4*self.pre2_4.height + self.no_of_pre3x8*self.pre3_8.height
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
# Find the left-most predecoder
min_x = 0
@@ -166,7 +165,7 @@ class hierarchical_decoder(design.design):
min_x = min(min_x, -self.pre2_4.width)
if self.no_of_pre3x8 > 0:
min_x = min(min_x, -self.pre3_8.width)
input_offset=vector(min_x - self.input_routing_width,0)
input_offset=vector(min_x - self.input_routing_width, 0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="m2",
@@ -177,7 +176,6 @@ class hierarchical_decoder(design.design):
self.route_input_to_predecodes()
def route_input_to_predecodes(self):
""" Route the vertical input rail to the predecoders """
for pre_num in range(self.no_of_pre2x4):
@@ -191,11 +189,10 @@ class hierarchical_decoder(design.design):
# To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_rail(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8):
for i in range(3):
@@ -208,11 +205,10 @@ class hierarchical_decoder(design.design):
# To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_rail(decoder_offset, input_offset)
self.route_input_rail(decoder_offset, input_offset)
def route_input_rail(self, input_offset, output_offset):
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
@@ -222,7 +218,6 @@ class hierarchical_decoder(design.design):
self.add_via_center(layers=self.m2_stack,
offset=output_offset)
self.add_path(("m3"), [input_offset, output_offset])
def add_pins(self):
""" Add the module pins """
@@ -235,7 +230,6 @@ class hierarchical_decoder(design.design):
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
@@ -245,7 +239,7 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8):
self.create_pre3x8(i)
def create_pre2x4(self,num):
def create_pre2x4(self, num):
""" Add a 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2):
@@ -265,8 +259,7 @@ class hierarchical_decoder(design.design):
mod=self.pre2_4))
self.connect_inst(pins)
def create_pre3x8(self,num):
def create_pre3x8(self, num):
""" Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """
# If we had 2x4 predecodes, those are used as the lower
# decode output bits
@@ -280,11 +273,10 @@ class hierarchical_decoder(design.design):
pins.append("out_{0}".format(output_index + out_index_offset))
pins.extend(["vdd", "gnd"])
self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num),
self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num),
mod=self.pre3_8))
self.connect_inst(pins)
def place_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
@@ -294,196 +286,131 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8):
self.place_pre3x8(i)
def place_pre2x4(self,num):
def place_pre2x4(self, num):
""" Place 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2):
base = vector(-self.pre2_4.width,0)
base = vector(-self.pre2_4.width, 0)
else:
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
self.pre2x4_inst[num].place(base)
def place_pre3x8(self,num):
def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3):
offset = vector(-self.pre_3_8.width,0)
mirror ="R0"
offset = vector(-self.pre_3_8.width, 0)
mirror = "R0"
else:
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset)
def create_row_decoder(self):
""" Create the row-decoder by placing NAND2/NAND3 and Inverters
""" Create the row-decoder by placing AND2/AND3 and Inverters
and add the primary decoder output pins. """
if (self.num_inputs >= 4):
self.create_decoder_nand_array()
self.create_decoder_inv_array()
self.create_decoder_and_array()
def create_decoder_nand_array(self):
""" Add a column of NAND gates for final decode """
def create_decoder_and_array(self):
""" Add a column of AND gates for final decode """
self.nand_inst = []
self.and_inst = []
# Row Decoder NAND GATE array for address inputs <5.
# Row Decoder AND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])):
row = len(self.predec_groups[0])*j + i
row = len(self.predec_groups[0]) * j + i
if (row < self.rows):
name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand2))
name = self.AND_FORMAT.format(row)
self.and_inst.append(self.add_inst(name=name,
mod=self.and2))
pins =["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])),
"Z_{0}".format(row),
"decode_{0}".format(row),
"vdd", "gnd"]
self.connect_inst(pins)
# Row Decoder NAND GATE array for address inputs >5.
# Row Decoder AND GATE array for address inputs >5.
elif (self.num_inputs > 5):
for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])):
row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0])*j + i
row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0]) * j + i
if (row < self.rows):
name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand3))
name = self.AND_FORMAT.format(row)
self.and_inst.append(self.add_inst(name=name,
mod=self.and3))
pins = ["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])),
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
"Z_{0}".format(row),
"decode_{0}".format(row),
"vdd", "gnd"]
self.connect_inst(pins)
def create_decoder_inv_array(self):
"""
Add a column of INV gates for the decoder.
"""
self.inv_inst = []
for row in range(self.rows):
name = self.INV_FORMAT.format(row)
self.inv_inst.append(self.add_inst(name=name,
mod=self.inv))
self.connect_inst(args=["Z_{0}".format(row),
"decode_{0}".format(row),
"vdd", "gnd"])
def place_decoder_inv_array(self):
"""
Place the column of INV gates for the decoder above the predecoders
and to the right of the NAND decoders.
"""
z_pin = self.inv.get_pin("Z")
if (self.num_inputs == 4 or self.num_inputs == 5):
x_off = self.internal_routing_width + self.nand2.width
else:
x_off = self.internal_routing_width + self.nand3.width
for row in range(self.rows):
if (row % 2 == 0):
inv_row_height = self.inv.height * row
mirror = "R0"
y_dir = 1
else:
inv_row_height = self.inv.height * (row + 1)
mirror = "MX"
y_dir = -1
y_off = inv_row_height
offset = vector(x_off,y_off)
self.inv_inst[row].place(offset=offset,
mirror=mirror)
def place_row_decoder(self):
"""
Place the row-decoder by placing NAND2/NAND3 and Inverters
and add the primary decoder output pins.
"""
Place the row-decoder by placing AND2/AND3 and Inverters
and add the primary decoder output pins.
"""
if (self.num_inputs >= 4):
self.place_decoder_nand_array()
self.place_decoder_inv_array()
self.place_decoder_and_array()
self.route_decoder()
def place_decoder_nand_array(self):
""" Add a column of NAND gates for final decode """
def place_decoder_and_array(self):
""" Add a column of AND gates for final decode """
# Row Decoder NAND GATE array for address inputs <5.
# Row Decoder AND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5):
self.place_nand_array(nand_mod=self.nand2)
self.place_and_array(and_mod=self.and2)
# Row Decoder NAND GATE array for address inputs >5.
# Row Decoder AND GATE array for address inputs >5.
# FIXME: why this correct offset?)
elif (self.num_inputs > 5):
self.place_nand_array(nand_mod=self.nand3)
self.place_and_array(and_mod=self.and3)
def place_nand_array(self, nand_mod):
""" Add a column of NAND gates for the decoder above the predecoders."""
def place_and_array(self, and_mod):
""" Add a column of AND gates for the decoder above the predecoders."""
for row in range(self.rows):
name = self.NAND_FORMAT.format(row)
if ((row % 2) == 0):
y_off = nand_mod.height*row
y_dir = 1
y_off = and_mod.height * row
mirror = "R0"
else:
y_off = nand_mod.height*(row + 1)
y_dir = -1
y_off = and_mod.height * (row + 1)
mirror = "MX"
self.nand_inst[row].place(offset=[self.internal_routing_width, y_off],
mirror=mirror)
self.and_inst[row].place(offset=[self.internal_routing_width, y_off],
mirror=mirror)
def route_decoder(self):
""" Route the nand to inverter in the decoder and add the pins. """
""" Add the pins. """
for row in range(self.rows):
# route nand output to output inv input
zr_pos = self.nand_inst[row].get_pin("Z").rc()
al_pos = self.inv_inst[row].get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pin = self.inv_inst[row].get_pin("Z")
z_pin = self.and_inst[row].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(row),
layer="m1",
offset=z_pin.ll(),
width=z_pin.width(),
height=z_pin.height())
def route_predecode_rails(self):
""" Creates vertical metal 2 rails to connect predecoder and decoder stages."""
# This is not needed for inputs <4 since they have no pre/decode stages.
if (self.num_inputs >= 4):
input_offset = vector(0.5*self.m2_width,0)
input_offset = vector(0.5 * self.m2_width, 0)
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
self.predecode_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=self.height)
self.route_rails_to_predecodes()
self.route_rails_to_decoder()
@@ -497,8 +424,7 @@ class hierarchical_decoder(design.design):
predecode_name = "predecode_{}".format(pre_num * 4 + i)
out_name = "out_{}".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.route_predecode_rail_m3(predecode_name, pin)
self.route_predecode_rail_m3(predecode_name, pin)
# FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre3x8):
@@ -506,17 +432,15 @@ class hierarchical_decoder(design.design):
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
out_name = "out_{}".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
self.route_predecode_rail_m3(predecode_name, pin)
self.route_predecode_rail_m3(predecode_name, pin)
def route_rails_to_decoder(self):
""" Use the self.predec_groups to determine the connections to the decoder NAND gates.
Inputs of NAND2/NAND3 gates come from different groups.
""" Use the self.predec_groups to determine the connections to the decoder AND gates.
Inputs of AND2/AND3 gates come from different groups.
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
[8,9,10,11,12,13,14,15] ] the first NAND3 inputs are connected to
[0,4,8] and second NAND3 is connected to [0,4,9] ........... and the
128th NAND3 is connected to [3,7,15]
[8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
[0,4,8] and second AND3 is connected to [0,4,9] ........... and the
128th AND3 is connected to [3,7,15]
"""
row_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5):
@@ -525,9 +449,9 @@ class hierarchical_decoder(design.design):
# FIXME: convert to connect_bus?
if (row_index < self.rows):
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
row_index = row_index + 1
elif (self.num_inputs > 5):
@@ -536,61 +460,48 @@ class hierarchical_decoder(design.design):
for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus?
if (row_index < self.rows):
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
predecode_name = "predecode_{}".format(index_C)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
predecode_name = "predecode_{}".format(index_C)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C"))
row_index = row_index + 1
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
# The vias will be placed in the center and right of the cells, respectively.
xoffset = self.nand_inst[0].rx()
for num in range(0,self.rows):
xoffset = self.and_inst[0].rx()
for num in range(0, self.rows):
for pin_name in ["vdd", "gnd"]:
# The nand and inv are the same height rows...
supply_pin = self.nand_inst[num].get_pin(pin_name)
supply_pin = self.and_inst[num].get_pin(pin_name)
pin_pos = vector(xoffset, supply_pin.cy())
self.add_power_pin(name=pin_name,
loc=pin_pos)
# Make a redundant rail too
for num in range(0,self.rows,2):
for pin_name in ["vdd", "gnd"]:
start = self.nand_inst[num].get_pin(pin_name).lc()
end = self.inv_inst[num].get_pin(pin_name).rc()
mid = (start+end).scale(0.5,0.5)
self.add_rect_center(layer="m1",
offset=mid,
width=end.x-start.x)
# Copy the pins from the predecoders
for pre in self.pre2x4_inst + self.pre3x8_inst:
self.copy_layout_pin(pre, "vdd")
self.copy_layout_pin(pre, "gnd")
def route_predecode_rail(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y)
self.add_path("m1", [rail_pos, pin.lc()])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
def route_predecode_rail_m3(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """
# This routes the pin up to the rail, basically, to avoid conflicts.
# It would be fixed with a channel router.
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y)
mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2)
rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y)
self.add_via_center(layers=self.m1_stack,
offset=pin.center())
self.add_wire(("m3","via2","m2"), [rail_pos, mid_point, pin.uc()])
self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos)
+62 -105
View File
@@ -32,61 +32,58 @@ class hierarchical_predecode(design.design):
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Add the INV and NAND gate modules """
""" Add the INV and AND gate modules """
self.inv = factory.create(module_type="pinv",
height=self.cell_height)
self.add_mod(self.inv)
self.add_nand(self.number_of_inputs)
self.add_mod(self.nand)
self.add_and(self.number_of_inputs)
self.add_mod(self.and_mod)
def add_nand(self, inputs):
def add_and(self, inputs):
""" Create the NAND for the predecode input stage """
if inputs==2:
self.nand = factory.create(module_type="pnand2",
height=self.cell_height)
self.and_mod = factory.create(module_type="pand2",
height=self.cell_height)
elif inputs==3:
self.nand = factory.create(module_type="pnand3",
height=self.cell_height)
self.and_mod = factory.create(module_type="pand3",
height=self.cell_height)
else:
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
def setup_layout_constraints(self):
self.height = self.number_of_outputs * self.nand.height
self.height = self.number_of_outputs * self.and_mod.height
# x offset for input inverters
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
# x offset to output inverters
self.x_off_inv_2 = self.x_off_nand + self.nand.width
# Height width are computed
self.width = self.x_off_inv_2 + self.inv.width
self.width = self.x_off_and + self.and_mod.width
def route_rails(self):
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
offset = vector(0.5*self.m2_width,2*self.m1_width)
offset = vector(0.5 * self.m2_width, self.m1_pitch)
self.input_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=offset,
names=input_names,
length=self.height - 2*self.m1_width)
length=self.height - 2 * self.m1_pitch)
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
decode_names = invert_names + non_invert_names
offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 2*self.m1_width)
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m1_pitch)
self.decode_rails = self.create_vertical_bus(layer="m2",
pitch=self.m2_pitch,
offset=offset,
names=decode_names,
length=self.height - 2*self.m1_width)
length=self.height - 2 * self.m1_pitch)
def create_input_inverters(self):
""" Create the input inverters to invert input signals for the decode stage. """
@@ -112,62 +109,35 @@ class hierarchical_predecode(design.design):
self.in_inst[inv_num].place(offset=offset,
mirror=mirror)
def create_output_inverters(self):
""" Create inverters for the inverted output decode signals. """
self.inv_inst = []
for inv_num in range(self.number_of_outputs):
name = "pre_nand_inv_{}".format(inv_num)
self.inv_inst.append(self.add_inst(name=name,
mod=self.inv))
self.connect_inst(["Z_{}".format(inv_num),
"out_{}".format(inv_num),
"vdd", "gnd"])
def create_and_array(self, connections):
""" Create the AND stage for the decodes """
self.and_inst = []
for and_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
name = "Xpre{0}_and_{1}".format(inout, and_input)
self.and_inst.append(self.add_inst(name=name,
mod=self.and_mod))
self.connect_inst(connections[and_input])
def place_output_inverters(self):
""" Place inverters for the inverted output decode signals. """
for inv_num in range(self.number_of_outputs):
if (inv_num % 2 == 0):
y_off = inv_num * self.inv.height
def place_and_array(self):
""" Place the AND stage for the decodes """
for and_input in range(self.number_of_outputs):
# inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
if (and_input % 2 == 0):
y_off = and_input * self.and_mod.height
mirror = "R0"
else:
y_off =(inv_num + 1)*self.inv.height
y_off = (and_input + 1) * self.and_mod.height
mirror = "MX"
offset = vector(self.x_off_inv_2, y_off)
self.inv_inst[inv_num].place(offset=offset,
mirror=mirror)
def create_nand_array(self,connections):
""" Create the NAND stage for the decodes """
self.nand_inst = []
for nand_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
name = "Xpre{0}_nand_{1}".format(inout,nand_input)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand))
self.connect_inst(connections[nand_input])
def place_nand_array(self):
""" Place the NAND stage for the decodes """
for nand_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
if (nand_input % 2 == 0):
y_off = nand_input * self.inv.height
mirror = "R0"
else:
y_off = (nand_input + 1) * self.inv.height
mirror = "MX"
offset = vector(self.x_off_nand, y_off)
self.nand_inst[nand_input].place(offset=offset,
mirror=mirror)
offset = vector(self.x_off_and, y_off)
self.and_inst[and_input].place(offset=offset,
mirror=mirror)
def route(self):
self.route_input_inverters()
self.route_inputs_to_rails()
self.route_nand_to_rails()
self.route_output_inverters()
self.route_and_to_rails()
self.route_output_and()
self.route_vdd_gnd()
def route_inputs_to_rails(self):
@@ -175,39 +145,30 @@ class hierarchical_predecode(design.design):
for num in range(self.number_of_inputs):
# route one signal next to each vdd/gnd rail since this is
# typically where the p/n devices are and there are no
# pins in the nand gates.
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
in_pin = "in_{}".format(num)
# pins in the and gates.
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
in_pin = "in_{}".format(num)
a_pin = "A_{}".format(num)
in_pos = vector(self.input_rails[in_pin].x,y_offset)
a_pos = vector(self.decode_rails[a_pin].x,y_offset)
self.add_path("m1",[in_pos, a_pos])
self.add_via_center(layers = self.m1_stack,
in_pos = vector(self.input_rails[in_pin].x, y_offset)
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
self.add_path("m1", [in_pos, a_pos])
self.add_via_center(layers=self.m1_stack,
offset=[self.input_rails[in_pin].x, y_offset])
self.add_via_center(layers = self.m1_stack,
self.add_via_center(layers=self.m1_stack,
offset=[self.decode_rails[a_pin].x, y_offset])
def route_output_inverters(self):
def route_output_and(self):
"""
Route all conections of the outputs inverters
Route all conections of the outputs and gates
"""
for num in range(self.number_of_outputs):
# route nand output to output inv input
zr_pos = self.nand_inst[num].get_pin("Z").rc()
al_pos = self.inv_inst[num].get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pin = self.inv_inst[num].get_pin("Z")
z_pin = self.and_inst[num].get_pin("Z")
self.add_layout_pin(text="out_{}".format(num),
layer="m1",
offset=z_pin.ll(),
height=z_pin.height(),
width=z_pin.width())
def route_input_inverters(self):
"""
@@ -219,7 +180,7 @@ class hierarchical_predecode(design.design):
#add output so that it is just below the vdd or gnd rail
# since this is where the p/n devices are and there are no
# pins in the nand gates.
# pins in the and gates.
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
@@ -236,13 +197,12 @@ class hierarchical_predecode(design.design):
self.add_via_center(layers=self.m1_stack,
offset=in_pos)
def route_nand_to_rails(self):
# This 2D array defines the connection mapping
nand_input_line_combination = self.get_nand_input_line_combination()
def route_and_to_rails(self):
# This 2D array defines the connection mapping
and_input_line_combination = self.get_and_input_line_combination()
for k in range(self.number_of_outputs):
# create x offset list
index_lst= nand_input_line_combination[k]
# create x offset list
index_lst= and_input_line_combination[k]
if self.number_of_inputs == 2:
gate_lst = ["A","B"]
@@ -251,35 +211,32 @@ class hierarchical_predecode(design.design):
# this will connect pins A,B or A,B,C
for rail_pin,gate_pin in zip(index_lst,gate_lst):
pin_pos = self.nand_inst[k].get_pin(gate_pin).lc()
pin_pos = self.and_inst[k].get_pin(gate_pin).lc()
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
# Find the x offsets for where the vias/pins should be placed
in_xoffset = self.in_inst[0].rx() + self.m1_space
out_xoffset = self.inv_inst[0].lx() - self.m1_space
for num in range(0,self.number_of_outputs):
# out_xoffset = self.and_inst[0].cx() + self.m1_space
for num in range(0, self.number_of_outputs):
# this will result in duplicate polygons for rails, but who cares
# Route both supplies
for n in ["vdd", "gnd"]:
nand_pin = self.nand_inst[num].get_pin(n)
supply_offset = nand_pin.ll().scale(0,1)
and_pin = self.and_inst[num].get_pin(n)
supply_offset = and_pin.ll().scale(0, 1)
self.add_rect(layer="m1",
offset=supply_offset,
width=self.inv_inst[num].rx())
width=self.and_inst[num].rx())
# Add pins in two locations
for xoffset in [in_xoffset, out_xoffset]:
pin_pos = vector(xoffset, nand_pin.cy())
for xoffset in [in_xoffset]:
pin_pos = vector(xoffset, and_pin.cy())
self.add_power_pin(n, pin_pos)
+11 -13
View File
@@ -27,33 +27,31 @@ class hierarchical_predecode2x4(hierarchical_predecode):
self.add_pins()
self.add_modules()
self.create_input_inverters()
self.create_output_inverters()
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"],
["in_0", "inbar_1", "Z_1", "vdd", "gnd"],
["inbar_0", "in_1", "Z_2", "vdd", "gnd"],
["in_0", "in_1", "Z_3", "vdd", "gnd"]]
self.create_nand_array(connections)
connections =[["inbar_0", "inbar_1", "out_0", "vdd", "gnd"],
["in_0", "inbar_1", "out_1", "vdd", "gnd"],
["inbar_0", "in_1", "out_2", "vdd", "gnd"],
["in_0", "in_1", "out_3", "vdd", "gnd"]]
self.create_and_array(connections)
def create_layout(self):
""" The general organization is from left to right:
1) a set of M2 rails for input signals
2) a set of inverters to invert input signals
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
4) a set of NAND gates for inversion
4) a set of AND gates for inversion
"""
self.setup_layout_constraints()
self.route_rails()
self.place_input_inverters()
self.place_output_inverters()
self.place_nand_array()
self.place_and_array()
self.route()
self.add_boundary()
self.DRC_LVS()
self.DRC_LVS()
def get_nand_input_line_combination(self):
""" These are the decoder connections of the NAND gates to the A,B pins """
def get_and_input_line_combination(self):
""" These are the decoder connections of the AND gates to the A,B pins """
combination = [["Abar_0", "Abar_1"],
["A_0", "Abar_1"],
["Abar_0", "A_1"],
["A_0", "A_1"]]
return combination
return combination
+15 -17
View File
@@ -20,26 +20,25 @@ class hierarchical_predecode3x8(hierarchical_predecode):
hierarchical_predecode.__init__(self, name, 3, height)
self.create_netlist()
if not OPTS.netlist_only:
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_input_inverters()
self.create_output_inverters()
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "Z_1", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "Z_2", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "Z_3", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "Z_4", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "Z_5", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "Z_6", "vdd", "gnd"],
["in_0", "in_1", "in_2", "Z_7", "vdd", "gnd"]]
self.create_nand_array(connections)
connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
self.create_and_array(connections)
def create_layout(self):
"""
"""
The general organization is from left to right:
1) a set of M2 rails for input signals
2) a set of inverters to invert input signals
@@ -49,20 +48,19 @@ class hierarchical_predecode3x8(hierarchical_predecode):
self.setup_layout_constraints()
self.route_rails()
self.place_input_inverters()
self.place_output_inverters()
self.place_nand_array()
self.place_and_array()
self.route()
self.add_boundary()
self.DRC_LVS()
def get_nand_input_line_combination(self):
def get_and_input_line_combination(self):
""" These are the decoder connections of the NAND gates to the A,B,C pins """
combination = [["Abar_0", "Abar_1", "Abar_2"],
["A_0", "Abar_1", "Abar_2"],
["Abar_0", "A_1", "Abar_2"],
["A_0", "A_1", "Abar_2"],
["Abar_0", "Abar_1", "A_2"],
["Abar_0", "Abar_1", "A_2"],
["A_0", "Abar_1", "A_2"],
["Abar_0", "A_1", "A_2"],
["A_0", "A_1", "A_2"]]
return combination
return combination
+166 -127
View File
@@ -1,17 +1,17 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved.
#
import sys
from tech import drc, parameter
from tech import drc
import debug
import design
from sram_factory import factory
from collections import namedtuple
from vector import vector
from globals import OPTS
class port_data(design.design):
"""
Create the data port (column mux, sense amps, write driver, etc.) for the given port number.
@@ -23,18 +23,21 @@ class port_data(design.design):
sram_config.set_local_config(self)
self.port = port
if self.write_size is not None:
self.num_wmasks = int(self.word_size/self.write_size)
self.num_wmasks = int(self.word_size / self.write_size)
else:
self.num_wmasks = 0
if name == "":
name = "port_data_{0}".format(self.port)
design.design.__init__(self, name)
debug.info(2, "create data port of size {0} with {1} words per row".format(self.word_size,self.words_per_row))
debug.info(2,
"create data port of size {0} with {1} words per row".format(self.word_size,
self.words_per_row))
self.create_netlist()
if not OPTS.netlist_only:
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
debug.check(len(self.all_ports)<=2,
"Bank layout cannot handle more than two ports.")
self.create_layout()
self.add_boundary()
@@ -46,7 +49,19 @@ class port_data(design.design):
# br lines are connect from the precharger
return self.precharge.get_br_names()
def get_bl_name(self, port=0):
bl_name = "bl"
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self, port=0):
br_name = "br"
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
def create_netlist(self):
self.precompute_constants()
@@ -80,8 +95,6 @@ class port_data(design.design):
else:
self.column_mux_array_inst = None
def create_layout(self):
self.compute_instance_offsets()
self.place_instances()
@@ -91,33 +104,32 @@ class port_data(design.design):
def add_pins(self):
""" Adding pins for port address module"""
self.add_pin("rbl_bl","INOUT")
self.add_pin("rbl_br","INOUT")
self.add_pin("rbl_bl", "INOUT")
self.add_pin("rbl_br", "INOUT")
for bit in range(self.num_cols):
bl_name = self.precharge_array.get_bl_name(self.port)
br_name = self.precharge_array.get_br_name(self.port)
self.add_pin("{0}_{1}".format(bl_name, bit),"INOUT")
self.add_pin("{0}_{1}".format(br_name, bit),"INOUT")
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
self.add_pin("{0}_{1}".format(bl_name, bit), "INOUT")
self.add_pin("{0}_{1}".format(br_name, bit), "INOUT")
if self.port in self.read_ports:
for bit in range(self.word_size):
self.add_pin("dout_{}".format(bit),"OUTPUT")
self.add_pin("dout_{}".format(bit), "OUTPUT")
if self.port in self.write_ports:
for bit in range(self.word_size):
self.add_pin("din_{}".format(bit),"INPUT")
self.add_pin("din_{}".format(bit), "INPUT")
# Will be empty if no col addr lines
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
for pin_name in sel_names:
self.add_pin(pin_name,"INPUT")
self.add_pin(pin_name, "INPUT")
if self.port in self.read_ports:
self.add_pin("s_en", "INPUT")
self.add_pin("p_en_bar", "INPUT")
if self.port in self.write_ports:
self.add_pin("w_en", "INPUT")
for bit in range(self.num_wmasks):
self.add_pin("bank_wmask_{}".format(bit),"INPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.add_pin("bank_wmask_{}".format(bit), "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def route_layout(self):
""" Create routing among the modules """
@@ -159,8 +171,8 @@ class port_data(design.design):
""" Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts:
self.copy_power_pins(inst,"vdd")
self.copy_power_pins(inst,"gnd")
self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst, "gnd")
def add_modules(self):
@@ -180,7 +192,6 @@ class port_data(design.design):
else:
self.sense_amp_array = None
if self.col_addr_size > 0:
self.column_mux_array = factory.create(module_type="column_mux_array",
columns=self.num_cols,
@@ -191,7 +202,6 @@ class port_data(design.design):
else:
self.column_mux_array = None
if self.port in self.write_ports:
self.write_driver_array = factory.create(module_type="write_driver_array",
columns=self.num_cols,
@@ -221,29 +231,27 @@ class port_data(design.design):
else:
self.num_col_addr_lines = 0
# A space for wells or jogging m2 between modules
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3 * self.m2_pitch)
# create arrays of bitline and bitline_bar names for read, write, or all ports
# create arrays of bitline and bitline_bar names for read,
# write, or all ports
self.bitcell = factory.create(module_type="bitcell")
self.bl_names = self.bitcell.get_all_bl_names()
self.br_names = self.bitcell.get_all_br_names()
self.wl_names = self.bitcell.get_all_wl_names()
# used for bl/br names
self.precharge = factory.create(module_type="precharge",
bitcell_bl = self.bl_names[0],
bitcell_br = self.br_names[0])
bitcell_bl=self.bl_names[0],
bitcell_br=self.br_names[0])
# We create a dummy here to get bl/br names to add those pins to this
# module, which happens before we create the real precharge_array
self.precharge_array = factory.create(module_type="precharge_array",
columns=self.num_cols + 1,
bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port])
columns=self.num_cols + 1,
bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port])
def create_precharge_array(self):
""" Creating Precharge """
@@ -253,8 +261,8 @@ class port_data(design.design):
self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port),
mod=self.precharge_array)
bl_name = self.precharge_array.get_bl_name(self.port)
br_name = self.precharge_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
temp = []
# Use left BLs for RBL
@@ -272,21 +280,19 @@ class port_data(design.design):
temp.extend(["p_en_bar", "vdd"])
self.connect_inst(temp)
def place_precharge_array(self, offset):
""" Placing Precharge """
self.precharge_array_inst.place(offset=offset, mirror="MX")
def create_column_mux_array(self):
""" Creating Column Mux when words_per_row > 1 . """
self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port),
mod=self.column_mux_array)
bl_name = self.column_mux_array.get_bl_name(self.port)
br_name = self.column_mux_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
temp = []
for col in range(self.num_cols):
temp.append("{0}_{1}".format(bl_name, col))
@@ -301,7 +307,6 @@ class port_data(design.design):
temp.append("gnd")
self.connect_inst(temp)
def place_column_mux_array(self, offset):
""" Placing Column Mux when words_per_row > 1 . """
if self.col_addr_size == 0:
@@ -309,14 +314,13 @@ class port_data(design.design):
self.column_mux_array_inst.place(offset=offset, mirror="MX")
def create_sense_amp_array(self):
""" Creating Sense amp """
self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port),
mod=self.sense_amp_array)
bl_name = self.sense_amp_array.get_bl_name(self.port)
br_name = self.sense_amp_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
temp = []
for bit in range(self.word_size):
temp.append("dout_{}".format(bit))
@@ -327,22 +331,19 @@ class port_data(design.design):
temp.append("{0}_out_{1}".format(bl_name, bit))
temp.append("{0}_out_{1}".format(br_name, bit))
temp.extend(["s_en", "vdd", "gnd"])
self.connect_inst(temp)
def place_sense_amp_array(self, offset):
""" Placing Sense amp """
self.sense_amp_array_inst.place(offset=offset, mirror="MX")
def create_write_driver_array(self):
""" Creating Write Driver """
self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port),
mod=self.write_driver_array)
bl_name = self.write_driver_array.get_bl_name(self.port)
br_name = self.write_driver_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
temp = []
for bit in range(self.word_size):
@@ -365,12 +366,10 @@ class port_data(design.design):
self.connect_inst(temp)
def place_write_driver_array(self, offset):
""" Placing Write Driver """
self.write_driver_array_inst.place(offset=offset, mirror="MX")
def create_write_mask_and_array(self):
""" Creating Write Mask AND Array """
self.write_mask_and_array_inst = self.add_inst(name="write_mask_and_array{}".format(self.port),
@@ -385,12 +384,10 @@ class port_data(design.design):
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_write_mask_and_array(self, offset):
""" Placing Write Mask AND array """
self.write_mask_and_array_inst.place(offset=offset, mirror="MX")
def compute_instance_offsets(self):
"""
Compute the empty instance offsets for port0 and port1 (if needed)
@@ -445,19 +442,17 @@ class port_data(design.design):
if self.column_mux_offset:
self.place_column_mux_array(self.column_mux_offset)
def route_sense_amp_out(self, port):
""" Add pins for the sense amp output """
for bit in range(self.word_size):
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit))
self.add_layout_pin_rect_center(text="dout_{0}".format(bit),
layer=data_pin.layer,
layer=data_pin.layer,
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width())
def route_write_driver_in(self, port):
""" Connecting write driver """
@@ -466,7 +461,6 @@ class port_data(design.design):
din_name = "din_{}".format(row)
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
def route_write_mask_and_array_in(self, port):
""" Add pins for the write mask and array input """
@@ -475,7 +469,6 @@ class port_data(design.design):
bank_wmask_name = "bank_wmask_{}".format(bit)
self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name)
def route_write_mask_and_array_to_write_driver(self,port):
""" Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write
mask AND array output and via for write driver enable """
@@ -498,15 +491,15 @@ class port_data(design.design):
while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()):
loc += 1
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
debug.check(loc<=self.num_wmasks,"Couldn't route the write mask select.")
debug.check(loc<=self.num_wmasks,
"Couldn't route the write mask select.")
else:
# Stride by the write size rather than finding the next pin to the right
loc += self.write_size
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
beg_pos = wmask_out_pin.center()
middle_pos = vector(length,wmask_out_pin.cy())
middle_pos = vector(length, wmask_out_pin.cy())
end_pos = vector(length, wdriver_en_pin.cy())
# Add via for the write driver array's enable input
@@ -516,21 +509,22 @@ class port_data(design.design):
# Route between write mask AND array and write driver array
self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos])
def route_column_mux_to_precharge_array(self, port):
""" Routing of BL and BR between col mux and precharge array """
# Only do this if we have a column mux!
if self.col_addr_size==0:
return
inst1 = self.column_mux_array_inst
inst2 = self.precharge_array_inst
if self.port==0:
self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1)
else:
self.connect_bitlines(inst1, inst2, self.num_cols)
insn2_start_bit = 1 if self.port == 0 else 0
self.connect_bitlines(inst1=inst1,
inst2=inst2,
num_bits=self.num_cols,
inst2_start_bit=insn2_start_bit)
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """
@@ -539,47 +533,46 @@ class port_data(design.design):
if self.col_addr_size>0:
# Sense amp is connected to the col mux
inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
inst1_bls_templ = "{inst}_out_{bit}"
start_bit = 0
else:
# Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
inst1_bls_templ="{inst}_{bit}"
if self.port==0:
start_bit=1
else:
start_bit=0
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
self.channel_route_bitlines(inst1=inst1,
inst1_bls_template=inst1_bls_templ,
inst2=inst2,
num_bits=self.word_size,
inst1_start_bit=start_bit)
def route_write_driver_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """
inst2 = self.write_driver_array_inst
if self.col_addr_size>0:
# Write driver is connected to the col mux
inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
inst1_bls_templ = "{inst}_out_{bit}"
start_bit = 0
else:
# Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
inst1_bls_templ="{inst}_{bit}"
if self.port==0:
start_bit=1
else:
start_bit=0
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
self.channel_route_bitlines(inst1=inst1, inst2=inst2,
num_bits=self.word_size,
inst1_bls_template=inst1_bls_templ,
inst1_start_bit=start_bit)
def route_write_driver_to_sense_amp(self, port):
""" Routing of BL and BR between write driver and sense amp """
@@ -589,8 +582,9 @@ class port_data(design.design):
# These should be pitch matched in the cell library,
# but just in case, do a channel route.
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size)
self.channel_route_bitlines(inst1=inst1,
inst2=inst2,
num_bits=self.word_size)
def route_bitline_pins(self):
""" Add the bitline pins for the given port """
@@ -609,12 +603,15 @@ class port_data(design.design):
for bit in range(self.num_cols):
if self.precharge_array_inst:
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+bit_offset), "bl_{}".format(bit))
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit))
self.copy_layout_pin(self.precharge_array_inst,
"bl_{}".format(bit + bit_offset),
"bl_{}".format(bit))
self.copy_layout_pin(self.precharge_array_inst,
"br_{}".format(bit + bit_offset),
"br_{}".format(bit))
else:
debug.error("Didn't find precharge array.")
def route_control_pins(self):
""" Add the control pins: s_en, p_en_bar, w_en """
if self.precharge_array_inst:
@@ -635,65 +632,107 @@ class port_data(design.design):
if self.write_mask_and_array_inst:
self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en")
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
def _group_bitline_instances(self, inst1, inst2, num_bits,
inst1_bls_template,
inst1_start_bit,
inst2_bls_template,
inst2_start_bit):
"""
Route the bl and br of two modules using the channel router.
Groups all the parameters into a named tuple and seperates them into
top and bottom instances.
"""
inst_group = namedtuple('InstanceGroup', ('inst', 'bls_template',
'bl_name', 'br_name', 'start_bit'))
inst1_group = inst_group(inst1, inst1_bls_template,
inst1.mod.get_bl_name(),
inst1.mod.get_br_name(),
inst1_start_bit)
inst2_group = inst_group(inst2, inst2_bls_template,
inst2.mod.get_bl_name(),
inst2.mod.get_br_name(),
inst2_start_bit)
# 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, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
bot_inst_group = inst1_group
top_inst_group = inst2_group
else:
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
bot_inst_group = inst2_group
top_inst_group = inst1_group
return (bot_inst_group, top_inst_group)
def _get_bitline_pins(self, inst_group, bit):
"""
Extracts bl/br pins from an InstanceGroup based on the bit modifier.
"""
full_bl_name = inst_group.bls_template.format(
**{'inst': inst_group.bl_name,
'bit': inst_group.start_bit + bit}
)
full_br_name = inst_group.bls_template.format(
**{'inst': inst_group.br_name,
'bit': inst_group.start_bit + bit}
)
return (inst_group.inst.get_pin(full_bl_name),
inst_group.inst.get_pin(full_br_name))
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bls_template="{inst}_{bit}",
inst1_start_bit=0,
inst2_bls_template="{inst}_{bit}",
inst2_start_bit=0):
"""
Route the bl and br of two modules using the channel router.
"""
bot_inst_group, top_inst_group = self._group_bitline_instances(
inst1, inst2, num_bits,
inst1_bls_template, inst1_start_bit,
inst2_bls_template, inst2_start_bit)
# Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
offset = bot_inst_group.inst.ul() + vector(0, self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))]
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
top_names = self._get_bitline_pins(top_inst_group, bit)
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
inst1_bls_template="{inst}_{bit}",
inst1_start_bit=0,
inst2_bls_template="{inst}_{bit}",
inst2_start_bit=0):
"""
Connect the bl and br of two modules.
This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed).
"""
# 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, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
else:
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
bot_inst_group, top_inst_group = self._group_bitline_instances(
inst1, inst2, num_bits,
inst1_bls_template, inst1_start_bit,
inst2_bls_template, inst2_start_bit)
for col in range(num_bits):
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col+bottom_start_bit)).uc()
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col+bottom_start_bit)).uc()
top_bl = top_inst.get_pin(top_bl_name.format(col+top_start_bit)).bc()
top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc()
bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col)
top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col)
bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc()
top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc()
yoffset = 0.5*(top_bl.y+bottom_bl.y)
self.add_path("m2",[bottom_bl, vector(bottom_bl.x,yoffset),
vector(top_bl.x,yoffset), top_bl])
self.add_path("m2",[bottom_br, vector(bottom_br.x,yoffset),
vector(top_br.x,yoffset), top_br])
yoffset = 0.5 * (top_bl.y + bot_bl.y)
self.add_path("m2", [bot_bl,
vector(bot_bl.x, yoffset),
vector(top_bl.x, yoffset),
top_bl])
self.add_path("m2", [bot_br,
vector(bot_br.x, yoffset),
vector(top_br.x, yoffset),
top_br])
def graph_exclude_precharge(self):
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
+13 -31
View File
@@ -12,6 +12,7 @@ from vector import vector
from sram_factory import factory
from globals import OPTS
class precharge_array(design.design):
"""
Dynamically generated precharge array of all bitlines. Cols is number
@@ -32,20 +33,13 @@ class precharge_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
def get_bl_name(self):
bl_name = self.pc_cell.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
return bl_name
def get_br_name(self, port=0):
def get_br_name(self):
br_name = self.pc_cell.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
return br_name
def add_pins(self):
"""Adds pins for spice file"""
@@ -75,11 +69,8 @@ class precharge_array(design.design):
size=self.size,
bitcell_bl=self.bitcell_bl,
bitcell_br=self.bitcell_br)
self.add_mod(self.pc_cell)
def add_layout_pins(self):
self.add_layout_pin(text="en_bar",
@@ -93,20 +84,9 @@ class precharge_array(design.design):
for i in range(len(self.local_insts)):
inst = self.local_insts[i]
bl_pin = inst.get_pin("bl")
self.add_layout_pin(text="bl_{0}".format(i),
layer="m2",
offset=bl_pin.ll(),
width=drc("minwidth_m2"),
height=bl_pin.height())
br_pin = inst.get_pin("br")
self.add_layout_pin(text="br_{0}".format(i),
layer="m2",
offset=br_pin.ll(),
width=drc("minwidth_m2"),
height=bl_pin.height())
self.copy_layout_pin(inst, "bl", "bl_{0}".format(i))
self.copy_layout_pin(inst, "br", "br_{0}".format(i))
def create_insts(self):
"""Creates a precharge array by horizontally tiling the precharge cell"""
self.local_insts = []
@@ -119,7 +99,6 @@ class precharge_array(design.design):
self.local_insts.append(inst)
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"])
def place_insts(self):
""" Places precharge array by horizontally tiling the precharge cell"""
from tech import cell_properties
@@ -137,8 +116,11 @@ class precharge_array(design.design):
xoffset = xoffset + self.pc_cell.width
def get_en_cin(self):
"""Get the relative capacitance of all the clk connections in the precharge array"""
#Assume single port
"""
Get the relative capacitance of all the clk connections
in the precharge array
"""
# Assume single port
precharge_en_cin = self.pc_cell.get_en_cin()
return precharge_en_cin*self.columns
return precharge_en_cin * self.columns
+18 -6
View File
@@ -9,6 +9,7 @@ import design
import debug
import utils
from tech import GDS,layer, parameter,drc
from tech import cell_properties as props
from globals import OPTS
import logical_effort
@@ -19,8 +20,12 @@ class sense_amp(design.design):
the technology library.
Sense amplifier to read a pair of bit-lines.
"""
pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"]
pin_names = [props.sense_amp.pin.bl,
props.sense_amp.pin.br,
props.sense_amp.pin.dout,
props.sense_amp.pin.en,
props.sense_amp.pin.vdd,
props.sense_amp.pin.gnd]
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
@@ -30,10 +35,18 @@ class sense_amp(design.design):
pin_map = []
def get_bl_names(self):
return "bl"
return props.sense_amp.pin.bl
def get_br_names(self):
return "br"
return props.sense_amp.pin.br
@property
def dout_name(self):
return props.sense_amp.pin.dout
@property
def en_name(self):
return props.sense_amp.pin.en
def __init__(self, name):
design.design.__init__(self, name)
@@ -79,11 +92,10 @@ class sense_amp(design.design):
def get_enable_name(self):
"""Returns name used for enable net"""
#FIXME: A better programmatic solution to designate pins
enable_name = "en"
enable_name = self.en_name
debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name))
return enable_name
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
+31 -30
View File
@@ -33,20 +33,21 @@ class sense_amp_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self):
bl_name = "bl"
return bl_name
def get_bl_name(self, port=0):
bl_name = self.amp.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self):
br_name = "br"
return br_name
def get_br_name(self, port=0):
br_name = self.amp.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
@property
def data_name(self):
return "data"
@property
def en_name(self):
return "en"
def create_netlist(self):
self.add_modules()
@@ -69,10 +70,10 @@ class sense_amp_array(design.design):
def add_pins(self):
for i in range(0,self.word_size):
self.add_pin("data_{0}".format(i), "OUTPUT")
self.add_pin("bl_{0}".format(i), "INPUT")
self.add_pin("br_{0}".format(i), "INPUT")
self.add_pin("en", "INPUT")
self.add_pin(self.data_name + "_{0}".format(i), "OUTPUT")
self.add_pin(self.get_bl_name() + "_{0}".format(i), "INPUT")
self.add_pin(self.get_br_name() + "_{0}".format(i), "INPUT")
self.add_pin(self.en_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
@@ -92,10 +93,10 @@ class sense_amp_array(design.design):
name = "sa_d{0}".format(i)
self.local_insts.append(self.add_inst(name=name,
mod=self.amp))
self.connect_inst(["bl_{0}".format(i),
"br_{0}".format(i),
"data_{0}".format(i),
"en", "vdd", "gnd"])
self.connect_inst([self.get_bl_name() + "_{0}".format(i),
self.get_br_name() + "_{0}".format(i),
self.data_name + "_{0}".format(i),
self.en_name, "vdd", "gnd"])
def place_sense_amp_array(self):
from tech import cell_properties
@@ -135,22 +136,22 @@ class sense_amp_array(design.design):
start_layer="m2",
vertical=True)
bl_pin = inst.get_pin("bl")
br_pin = inst.get_pin("br")
dout_pin = inst.get_pin("dout")
self.add_layout_pin(text="bl_{0}".format(i),
bl_pin = inst.get_pin(inst.mod.get_bl_names())
br_pin = inst.get_pin(inst.mod.get_br_names())
dout_pin = inst.get_pin(inst.mod.dout_name)
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
layer="m2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
self.add_layout_pin(text="br_{0}".format(i),
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
layer="m2",
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
self.add_layout_pin(text="data_{0}".format(i),
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
layer="m2",
offset=dout_pin.ll(),
width=dout_pin.width(),
@@ -159,8 +160,8 @@ class sense_amp_array(design.design):
def route_rails(self):
# add sclk rail across entire array
sclk_offset = self.amp.get_pin("en").ll().scale(0,1)
self.add_layout_pin(text="en",
sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0,1)
self.add_layout_pin(text=self.en_name,
layer="m1",
offset=sclk_offset,
width=self.width,
@@ -37,20 +37,13 @@ class single_level_column_mux_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
def get_bl_name(self):
bl_name = self.mux.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
return bl_name
def get_br_name(self, port=0):
br_name = self.mux.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
return br_name
def create_netlist(self):
self.add_modules()
+10 -11
View File
@@ -75,8 +75,7 @@ class wordline_driver(design.design):
"""
# Find the x offsets for where the vias/pins should be placed
a_xoffset = self.nand_inst[0].rx()
b_xoffset = self.inv2_inst[0].lx()
a_xoffset = self.nand_inst[0].lx()
for num in range(self.rows):
# this will result in duplicate polygons for rails, but who cares
@@ -90,7 +89,7 @@ class wordline_driver(design.design):
supply_pin = self.inv2_inst[num].get_pin(name)
# Add pins in two locations
for xoffset in [a_xoffset, b_xoffset]:
for xoffset in [a_xoffset]:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_power_pin(name, pin_pos)
@@ -116,7 +115,7 @@ class wordline_driver(design.design):
"vdd", "gnd"])
def place_drivers(self):
nand2_xoffset = 2*self.m1_width + 5*self.m1_space
nand2_xoffset = 2 * self.m1_width + 5 * self.m1_space
inv2_xoffset = nand2_xoffset + self.nand2.width
self.width = inv2_xoffset + self.inv.width
@@ -124,10 +123,10 @@ class wordline_driver(design.design):
for row in range(self.rows):
if (row % 2):
y_offset = self.inv.height*(row + 1)
y_offset = self.inv.height * (row + 1)
inst_mirror = "MX"
else:
y_offset = self.inv.height*row
y_offset = self.inv.height * row
inst_mirror = "R0"
nand2_offset = [nand2_xoffset, y_offset]
@@ -169,8 +168,8 @@ class wordline_driver(design.design):
zr_pos = nand_inst.get_pin("Z").rc()
al_pos = inv2_inst.get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
mid1_pos = vector(0.5 * (zr_pos.x + al_pos.x), zr_pos.y)
mid2_pos = vector(0.5 * (zr_pos.x + al_pos.x), al_pos.y)
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
# connect the decoder input pin to nand2 B
@@ -181,7 +180,7 @@ class wordline_driver(design.design):
up_or_down = self.m2_space if row % 2 else -self.m2_space
input_offset = vector(0, b_pos.y + up_or_down)
base_offset = vector(clk_offset.x, input_offset.y)
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
mid_via_offset = base_offset + contact_offset
# must under the clk line in M1
@@ -208,7 +207,7 @@ class wordline_driver(design.design):
end=wl_offset - vector(self.m1_width, 0))
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
"""
"""
Follows the clk_buf to a wordline signal adding
each stages stage effort to a list.
"""
@@ -225,7 +224,7 @@ class wordline_driver(design.design):
return stage_effort_list
def get_wl_en_cin(self):
"""
"""
Get the relative capacitance of all
the enable connections in the bank
"""
+18 -3
View File
@@ -10,6 +10,7 @@ import design
import utils
from globals import OPTS
from tech import GDS,layer
from tech import cell_properties as props
class write_driver(design.design):
"""
@@ -19,7 +20,13 @@ class write_driver(design.design):
the technology library.
"""
pin_names = ["din", "bl", "br", "en", "vdd", "gnd"]
pin_names = [props.write_driver.pin.din,
props.write_driver.pin.bl,
props.write_driver.pin.br,
props.write_driver.pin.en,
props.write_driver.pin.vdd,
props.write_driver.pin.gnd]
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
@@ -38,10 +45,18 @@ class write_driver(design.design):
self.add_pin_types(self.type_list)
def get_bl_names(self):
return "bl"
return props.write_driver.pin.bl
def get_br_names(self):
return "br"
return props.write_driver.pin.br
@property
def din_name(self):
return props.write_driver.pin.din
@property
def en_name(self):
return props.write_driver.pin.en
def get_w_en_cin(self):
"""Get the relative capacitance of a single input"""
+42 -37
View File
@@ -37,19 +37,21 @@ class write_driver_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
bl_name = self.driver.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_bl_name(self):
bl_name = "bl"
return bl_name
def get_br_name(self, port=0):
br_name = self.driver.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
def get_br_name(self):
br_name = "br"
return br_name
@property
def data_name(self):
return "data"
@property
def en_name(self):
return "en"
def create_netlist(self):
self.add_modules()
@@ -71,15 +73,15 @@ class write_driver_array(design.design):
def add_pins(self):
for i in range(self.word_size):
self.add_pin("data_{0}".format(i), "INPUT")
for i in range(self.word_size):
self.add_pin("bl_{0}".format(i), "OUTPUT")
self.add_pin("br_{0}".format(i), "OUTPUT")
self.add_pin(self.data_name + "_{0}".format(i), "INPUT")
for i in range(self.word_size):
self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT")
self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT")
if self.write_size:
for i in range(self.num_wmasks):
self.add_pin("en_{0}".format(i), "INPUT")
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
else:
self.add_pin("en", "INPUT")
self.add_pin(self.en_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
@@ -102,20 +104,20 @@ class write_driver_array(design.design):
mod=self.driver)
if self.write_size:
self.connect_inst(["data_{0}".format(index),
"bl_{0}".format(index),
"br_{0}".format(index),
"en_{0}".format(windex), "vdd", "gnd"])
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(windex), "vdd", "gnd"])
w+=1
# when w equals write size, the next en pin can be connected since we are now at the next wmask bit
if w == self.write_size:
w = 0
windex+=1
else:
self.connect_inst(["data_{0}".format(index),
"bl_{0}".format(index),
"br_{0}".format(index),
"en", "vdd", "gnd"])
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name, "vdd", "gnd"])
def place_write_array(self):
@@ -140,21 +142,22 @@ class write_driver_array(design.design):
def add_layout_pins(self):
for i in range(self.word_size):
din_pin = self.driver_insts[i].get_pin("din")
self.add_layout_pin(text="data_{0}".format(i),
inst = self.driver_insts[i]
din_pin = inst.get_pin(inst.mod.din_name)
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
layer="m2",
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
bl_pin = self.driver_insts[i].get_pin("bl")
self.add_layout_pin(text="bl_{0}".format(i),
bl_pin = inst.get_pin(inst.mod.get_bl_names())
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
layer="m2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
br_pin = self.driver_insts[i].get_pin("br")
self.add_layout_pin(text="br_{0}".format(i),
br_pin = inst.get_pin(inst.mod.get_br_names())
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
layer="m2",
offset=br_pin.ll(),
width=br_pin.width(),
@@ -169,7 +172,8 @@ class write_driver_array(design.design):
start_layer = "m2")
if self.write_size:
for bit in range(self.num_wmasks):
en_pin = self.driver_insts[bit*self.write_size].get_pin("en")
inst = self.driver_insts[bit*self.write_size]
en_pin = inst.get_pin(inst.mod.en_name)
# Determine width of wmask modified en_pin with/without col mux
wmask_en_len = self.words_per_row*(self.write_size * self.driver_spacing)
if (self.words_per_row == 1):
@@ -177,15 +181,16 @@ class write_driver_array(design.design):
else:
en_gap = self.driver_spacing
self.add_layout_pin(text="en_{0}".format(bit),
self.add_layout_pin(text=self.en_name + "_{0}".format(bit),
layer=en_pin.layer,
offset=en_pin.ll(),
width=wmask_en_len-en_gap,
height=en_pin.height())
else:
self.add_layout_pin(text="en",
inst = self.driver_insts[0]
self.add_layout_pin(text=self.en_name,
layer="m1",
offset=self.driver_insts[0].get_pin("en").ll().scale(0,1),
offset=inst.get_pin(inst.mod.en_name).ll().scale(0,1),
width=self.width)