Merge branch 'wlbuffer' into dev

This commit is contained in:
mrg 2020-09-28 15:51:45 -07:00
commit 4a987bef9a
15 changed files with 226 additions and 92 deletions

View File

@ -63,6 +63,16 @@ class layout():
self.translate_all(offset) self.translate_all(offset)
return offset return offset
def offset_x_coordinates(self):
"""
This function is called after everything is placed to
shift the origin to the furthest left point.
Y offset is unchanged.
"""
offset = self.find_lowest_coords()
self.translate_all(offset.scale(1, 0))
return offset
def get_gate_offset(self, x_offset, height, inv_num): def get_gate_offset(self, x_offset, height, inv_num):
""" """
Gets the base offset and y orientation of stacked rows of gates Gets the base offset and y orientation of stacked rows of gates

View File

@ -223,10 +223,10 @@ class bank(design.design):
# UPPER LEFT QUADRANT # UPPER LEFT QUADRANT
# To the left of the bitcell array above the predecoders and control logic # To the left of the bitcell array above the predecoders and control logic
x_offset = self.m2_gap + self.port_address.width x_offset = self.m2_gap + self.port_address[port].width
self.port_address_offsets[port] = vector(-x_offset, self.port_address_offsets[port] = vector(-x_offset,
self.main_bitcell_array_bottom) self.main_bitcell_array_bottom)
self.predecoder_height = self.port_address.predecoder_height + self.port_address_offsets[port].y self.predecoder_height = self.port_address[port].predecoder_height + self.port_address_offsets[port].y
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver # Place the col decoder left aligned with wordline driver
@ -234,7 +234,7 @@ class bank(design.design):
# control logic to allow control signals to easily pass over in M3 # control logic to allow control signals to easily pass over in M3
# by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs
# may be routed in M3 or M4 # may be routed in M3 or M4
x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width x_offset = self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width
if self.col_addr_size > 0: if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = 1.25 * self.dff.height + self.column_decoder.height y_offset = 1.25 * self.dff.height + self.column_decoder.height
@ -267,7 +267,7 @@ class bank(design.design):
# LOWER RIGHT QUADRANT # LOWER RIGHT QUADRANT
# To the right of the bitcell array # To the right of the bitcell array
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
self.port_address_offsets[port] = vector(x_offset, self.port_address_offsets[port] = vector(x_offset,
self.main_bitcell_array_bottom) self.main_bitcell_array_bottom)
@ -278,7 +278,7 @@ class bank(design.design):
# control logic to allow control signals to easily pass over in M3 # control logic to allow control signals to easily pass over in M3
# by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs
# may be routed in M3 or M4 # may be routed in M3 or M4
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[port].wordline_driver_array.width
if self.col_addr_size > 0: if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.bitcell_array_top + 1.25 * self.dff.height + self.column_decoder.height y_offset = self.bitcell_array_top + 1.25 * self.dff.height + self.column_decoder.height
@ -366,12 +366,13 @@ class bank(design.design):
def add_modules(self): def add_modules(self):
""" Add all the modules using the class loader """ """ Add all the modules using the class loader """
self.port_address = factory.create(module_type="port_address", self.port_address = []
cols=self.num_cols + self.num_spare_cols, for port in self.all_ports:
rows=self.num_rows) self.port_address.append(factory.create(module_type="port_address",
self.add_mod(self.port_address) cols=self.num_cols + self.num_spare_cols,
rows=self.num_rows,
self.num_rbl = len(self.all_ports) port=port))
self.add_mod(self.port_address[port])
try: try:
local_array_size = OPTS.local_array_size local_array_size = OPTS.local_array_size
@ -420,13 +421,10 @@ class bank(design.design):
# gnd # gnd
temp = self.bitcell_array.get_inouts() temp = self.bitcell_array.get_inouts()
wordline_names = self.bitcell_array.get_inputs() temp.append("rbl_wl0")
temp.extend(self.bitcell_array.get_wordline_names())
# Rename the RBL WL to the enable name if len(self.all_ports) > 1:
for port in self.all_ports: temp.append("rbl_wl1")
rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port]
wordline_names = [x.replace(rbl_wl_name, "wl_en{0}".format(port)) for x in wordline_names]
temp.extend(wordline_names)
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
@ -486,13 +484,15 @@ class bank(design.design):
self.port_address_inst = [None] * len(self.all_ports) self.port_address_inst = [None] * len(self.all_ports)
for port in 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) mod=self.port_address[port])
temp = [] temp = []
for bit in range(self.row_addr_size): 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{}".format(port)) temp.append("wl_en{}".format(port))
temp.extend(self.bitcell_array.get_wordline_names(port)) wordline_names = self.bitcell_array.get_wordline_names(port)
temp.extend(wordline_names)
temp.append("rbl_wl{}".format(port))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
@ -845,35 +845,51 @@ class bank(design.design):
self.route_port_address_in(port) self.route_port_address_in(port)
if port % 2: if port % 2:
self.route_port_address_right(port) self.route_port_address_out(port, "right")
else: else:
self.route_port_address_left(port) self.route_port_address_out(port, "left")
def route_port_address_left(self, port): def route_port_address_out(self, port, side="left"):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port)): rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]):
# The mid guarantees we exit the input cell to the right. # The mid guarantees we exit the input cell to the right.
driver_wl_pin = self.port_address_inst[port].get_pin(driver_name) driver_wl_pin = self.port_address_inst[port].get_pin(driver_name)
driver_wl_pos = driver_wl_pin.rc() if side == "left":
driver_wl_pos = driver_wl_pin.rc()
else:
driver_wl_pos = driver_wl_pin.lc()
bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name) bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name)
bitcell_wl_pos = bitcell_wl_pin.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) if side == "left":
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1) bitcell_wl_pos = bitcell_wl_pin.lc()
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2]) port_address_pos = self.port_address_inst[port].rx()
# Via is non-preferred direction because mid1->mid2 is non-preferred direction bitcell_array_pos = self.bitcell_array_inst.lx()
self.add_via_stack_center(from_layer=driver_wl_pin.layer, else:
to_layer=bitcell_wl_pin.layer, bitcell_wl_pos = bitcell_wl_pin.rc()
offset=mid2, port_address_pos = self.port_address_inst[port].lx()
directions="nonpref") bitcell_array_pos = self.bitcell_array_inst.rx()
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
if driver_wl_pin.layer != bitcell_wl_pin.layer:
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2])
self.add_via_stack_center(from_layer=driver_wl_pin.layer,
to_layer=bitcell_wl_pin.layer,
offset=mid2)
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
else:
self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_port_address_right(self, port): def route_port_address_right(self, port):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port)): rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]):
# The mid guarantees we exit the input cell to the right. # The mid guarantees we exit the input cell to the right.
driver_wl_pin = self.port_address_inst[port].get_pin(driver_name) driver_wl_pin = self.port_address_inst[port].get_pin(driver_name)
driver_wl_pos = driver_wl_pin.lc() driver_wl_pos = driver_wl_pin.lc()
@ -886,6 +902,7 @@ class bank(design.design):
to_layer=bitcell_wl_pin.layer, to_layer=bitcell_wl_pin.layer,
offset=mid2) offset=mid2)
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
def route_column_address_lines(self, port): def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """ """ Connecting the select lines of column mux to the address bus """
@ -1021,10 +1038,6 @@ class bank(design.design):
connection.append((self.prefix + "p_en_bar{}".format(port), connection.append((self.prefix + "p_en_bar{}".format(port),
self.port_data_inst[port].get_pin("p_en_bar"))) self.port_data_inst[port].get_pin("p_en_bar")))
rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)
connection.append((self.prefix + "wl_en{}".format(port),
self.bitcell_array_inst.get_pin(rbl_wl_name[port])))
if port in self.write_ports: if port in self.write_ports:
connection.append((self.prefix + "w_en{}".format(port), connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en"))) self.port_data_inst[port].get_pin("w_en")))
@ -1045,7 +1058,7 @@ class bank(design.design):
self.add_via_stack_center(from_layer=pin.layer, self.add_via_stack_center(from_layer=pin.layer,
to_layer="m2", to_layer="m2",
offset=control_pos) offset=control_pos)
# clk to wordline_driver # clk to wordline_driver
control_signal = self.prefix + "wl_en{}".format(port) control_signal = self.prefix + "wl_en{}".format(port)
if port % 2: if port % 2:

View File

@ -54,6 +54,8 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
""" Add the modules used in this design """ """ Add the modules used in this design """
self.local_mods = [] self.local_mods = []
# Special case of a single local array
# so it should contain the left and possibly right RBL
if len(self.column_sizes) == 1: if len(self.column_sizes) == 1:
la = factory.create(module_type="local_bitcell_array", la = factory.create(module_type="local_bitcell_array",
rows=self.row_size, rows=self.row_size,
@ -66,19 +68,21 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
return return
for i, cols in enumerate(self.column_sizes): for i, cols in enumerate(self.column_sizes):
# Always add the left RBLs to the first subarray and the right RBLs to the last subarray # Always add the left RBLs to the first subarray
if i == 0: if i == 0:
la = factory.create(module_type="local_bitcell_array", la = factory.create(module_type="local_bitcell_array",
rows=self.row_size, rows=self.row_size,
cols=cols, cols=cols,
rbl=self.rbl, rbl=self.rbl,
left_rbl=[0]) left_rbl=[0])
# Add the right RBL to the last subarray
elif i == len(self.column_sizes) - 1 and len(self.all_ports) > 1: elif i == len(self.column_sizes) - 1 and len(self.all_ports) > 1:
la = factory.create(module_type="local_bitcell_array", la = factory.create(module_type="local_bitcell_array",
rows=self.row_size, rows=self.row_size,
cols=cols, cols=cols,
rbl=self.rbl, rbl=self.rbl,
right_rbl=[1]) right_rbl=[1])
# Middle subarrays do not have any RBLs
else: else:
la = factory.create(module_type="local_bitcell_array", la = factory.create(module_type="local_bitcell_array",
rows=self.row_size, rows=self.row_size,
@ -263,3 +267,4 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
for inst in self.local_insts: for inst in self.local_insts:
offsets.extend(inst.lx() + x for x in inst.mod.get_column_offsets()) offsets.extend(inst.lx() + x for x in inst.mod.get_column_offsets())
return offsets return offsets

View File

@ -55,9 +55,9 @@ class hierarchical_decoder(design.design):
self.route_decoder_bus() self.route_decoder_bus()
self.route_vdd_gnd() self.route_vdd_gnd()
self.offset_all_coordinates() self.offset_x_coordinates()
self.width = self.and_inst[0].rx() + self.m1_space self.width = self.and_inst[0].rx() + 0.5 * self.m1_width
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()

View File

@ -207,9 +207,9 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
y_offset = in_pin.cy() y_offset = in_pin.cy()
if port == 0: if port == 0:
y_offset -= 1.5 * self.m3_pitch y_offset -= 2 * self.m3_pitch
else: else:
y_offset += 1.5 * self.m3_pitch y_offset += 2 * self.m3_pitch
self.add_layout_pin_segment_center(text=wl_name, self.add_layout_pin_segment_center(text=wl_name,
layer="m3", layer="m3",

View File

@ -17,10 +17,11 @@ class port_address(design.design):
Create the address port (row decoder and wordline driver).. Create the address port (row decoder and wordline driver)..
""" """
def __init__(self, cols, rows, name=""): def __init__(self, cols, rows, port, name=""):
self.num_cols = cols self.num_cols = cols
self.num_rows = rows self.num_rows = rows
self.port = port
self.addr_size = ceil(log(self.num_rows, 2)) self.addr_size = ceil(log(self.num_rows, 2))
if name == "": if name == "":
@ -39,6 +40,7 @@ class port_address(design.design):
self.add_modules() self.add_modules()
self.create_row_decoder() self.create_row_decoder()
self.create_wordline_driver() self.create_wordline_driver()
self.create_rbl_driver()
def create_layout(self): def create_layout(self):
if "li" in layer: if "li" in layer:
@ -59,6 +61,8 @@ class port_address(design.design):
for bit in range(self.num_rows): for bit in range(self.num_rows):
self.add_pin("wl_{0}".format(bit), "OUTPUT") self.add_pin("wl_{0}".format(bit), "OUTPUT")
self.add_pin("rbl_wl", "OUTPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
@ -71,10 +75,13 @@ class port_address(design.design):
def route_supplies(self): def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts: for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]:
self.copy_power_pins(inst, "vdd") self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst, "gnd") self.copy_power_pins(inst, "gnd")
rbl_vdd_pin = self.rbl_driver_inst.get_pin("vdd")
self.add_power_pin("vdd", rbl_vdd_pin.lc())
def route_pins(self): def route_pins(self):
for row in range(self.addr_size): for row in range(self.addr_size):
decoder_name = "addr_{}".format(row) decoder_name = "addr_{}".format(row)
@ -82,16 +89,16 @@ class port_address(design.design):
for row in range(self.num_rows): for row in range(self.num_rows):
driver_name = "wl_{}".format(row) driver_name = "wl_{}".format(row)
self.copy_layout_pin(self.wordline_driver_inst, driver_name) self.copy_layout_pin(self.wordline_driver_array_inst, driver_name)
self.copy_layout_pin(self.wordline_driver_inst, "en", "wl_en") self.copy_layout_pin(self.rbl_driver_inst, "Z", "rbl_wl")
def route_internal(self): def route_internal(self):
for row in range(self.num_rows): for row in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs # The pre/post is to access the pin from "outside" the cell to avoid DRCs
decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row)) decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row))
decoder_out_pos = decoder_out_pin.rc() decoder_out_pos = decoder_out_pin.rc()
driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row)) driver_in_pin = self.wordline_driver_array_inst.get_pin("in_{}".format(row))
driver_in_pos = driver_in_pin.lc() driver_in_pos = driver_in_pin.lc()
self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3) self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3)
@ -102,6 +109,26 @@ class port_address(design.design):
self.add_via_stack_center(from_layer=driver_in_pin.layer, self.add_via_stack_center(from_layer=driver_in_pin.layer,
to_layer=self.route_layer, to_layer=self.route_layer,
offset=driver_in_pos) offset=driver_in_pos)
# Route the RBL from the enable input
en_pin = self.wordline_driver_array_inst.get_pin("en")
if self.port == 0:
en_pos = en_pin.bc()
else:
en_pos = en_pin.uc()
rbl_in_pin = self.rbl_driver_inst.get_pin("A")
rbl_in_pos = rbl_in_pin.center()
self.add_via_stack_center(from_layer=rbl_in_pin.layer,
to_layer=en_pin.layer,
offset=rbl_in_pos)
self.add_zjog(layer=en_pin.layer,
start=rbl_in_pos,
end=en_pos,
first_direction="V")
self.add_layout_pin_rect_center(text="wl_en",
layer=en_pin.layer,
offset=rbl_in_pos)
def add_modules(self): def add_modules(self):
@ -109,10 +136,33 @@ class port_address(design.design):
num_outputs=self.num_rows) num_outputs=self.num_rows)
self.add_mod(self.row_decoder) self.add_mod(self.row_decoder)
self.wordline_driver = factory.create(module_type="wordline_driver_array", self.wordline_driver_array = factory.create(module_type="wordline_driver_array",
rows=self.num_rows, rows=self.num_rows,
cols=self.num_cols) cols=self.num_cols)
self.add_mod(self.wordline_driver) self.add_mod(self.wordline_driver_array)
try:
local_array_size = OPTS.local_array_size
driver_size = max(int(self.num_cols / local_array_size), 1)
except AttributeError:
local_array_size = 0
# Defautl to FO4
driver_size = max(int(self.num_cols / 4), 1)
# The polarity must be switched if we have a hierarchical wordline
# to compensate for the local array inverters
b = factory.create(module_type="bitcell")
if local_array_size > 0:
self.rbl_driver = factory.create(module_type="inv_dec",
size=driver_size,
height=b.height)
else:
self.rbl_driver = factory.create(module_type="buf_dec",
size=driver_size,
height=b.height)
self.add_mod(self.rbl_driver)
def create_row_decoder(self): def create_row_decoder(self):
""" Create the hierarchical row decoder """ """ Create the hierarchical row decoder """
@ -128,11 +178,24 @@ class port_address(design.design):
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def create_rbl_driver(self):
""" Create the RBL Wordline Driver """
self.rbl_driver_inst = self.add_inst(name="rbl_driver",
mod=self.rbl_driver)
temp = []
temp.append("wl_en")
temp.append("rbl_wl")
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def create_wordline_driver(self): def create_wordline_driver(self):
""" Create the Wordline Driver """ """ Create the Wordline Driver """
self.wordline_driver_inst = self.add_inst(name="wordline_driver", self.wordline_driver_array_inst = self.add_inst(name="wordline_driver",
mod=self.wordline_driver) mod=self.wordline_driver_array)
temp = [] temp = []
for row in range(self.num_rows): for row in range(self.num_rows):
@ -150,11 +213,23 @@ class port_address(design.design):
""" """
row_decoder_offset = vector(0, 0) row_decoder_offset = vector(0, 0)
wordline_driver_offset = vector(self.row_decoder.width, 0)
self.wordline_driver_inst.place(wordline_driver_offset)
self.row_decoder_inst.place(row_decoder_offset) self.row_decoder_inst.place(row_decoder_offset)
wordline_driver_array_offset = vector(self.row_decoder_inst.rx(), 0)
self.wordline_driver_array_inst.place(wordline_driver_array_offset)
x_offset = self.wordline_driver_array_inst.rx() - self.rbl_driver.width - self.m1_pitch
if self.port == 0:
rbl_driver_offset = vector(x_offset,
0)
self.rbl_driver_inst.place(rbl_driver_offset, "MX")
else:
rbl_driver_offset = vector(x_offset,
self.wordline_driver_array.height)
self.rbl_driver_inst.place(rbl_driver_offset)
# Pass this up # Pass this up
self.predecoder_height = self.row_decoder.predecoder_height self.predecoder_height = self.row_decoder.predecoder_height
self.height = self.row_decoder.height self.height = self.row_decoder.height
self.width = self.wordline_driver_inst.rx() self.width = self.wordline_driver_array_inst.rx()

View File

@ -305,14 +305,21 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
def create_layout(self): def create_layout(self):
# We will need unused wordlines grounded, so we need to know their layer
pin = self.cell.get_pin(self.cell.get_all_wl_names()[0])
pin_layer = pin.layer
self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
self.unused_offset = vector(self.unused_pitch, 0)
# Add extra width on the left and right for the unused WLs
self.height = (self.row_size + self.extra_rows) * self.dummy_row.height self.height = (self.row_size + self.extra_rows) * self.dummy_row.height
self.width = (self.column_size + self.extra_cols) * self.cell.width self.width = (self.column_size + self.extra_cols) * self.cell.width + 2 * self.unused_pitch
# This is a bitcell x bitcell offset to scale # This is a bitcell x bitcell offset to scale
self.bitcell_offset = vector(self.cell.width, self.cell.height) self.bitcell_offset = vector(self.cell.width, self.cell.height)
# Everything is computed with the main array at (0, 0) to start # Everything is computed with the main array at (self.unused_pitch, 0) to start
self.bitcell_array_inst.place(offset=[0, 0]) self.bitcell_array_inst.place(offset=self.unused_offset)
self.add_replica_columns() self.add_replica_columns()
@ -356,7 +363,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# Grow from left to right, toward the array # Grow from left to right, toward the array
for bit, port in enumerate(self.left_rbl): for bit, port in enumerate(self.left_rbl):
offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.unused_offset
self.replica_col_insts[port].place(offset) self.replica_col_insts[port].place(offset)
# Grow to the right of the bitcell array, array outward # Grow to the right of the bitcell array, array outward
for bit, port in enumerate(self.right_rbl): for bit, port in enumerate(self.right_rbl):
@ -367,11 +374,13 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# Add the dummy rows even if we aren't adding the replica column to this bitcell array # Add the dummy rows even if we aren't adding the replica column to this bitcell array
# These grow up, toward the array # These grow up, toward the array
for bit in range(self.rbl[0]): for bit in range(self.rbl[0]):
self.dummy_row_replica_insts[bit].place(offset=self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2), dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset
self.dummy_row_replica_insts[bit].place(offset=dummy_offset,
mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0") mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0")
# These grow up, away from the array # These grow up, away from the array
for bit in range(self.rbl[1]): for bit in range(self.rbl[1]):
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul()
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset,
mirror="MX" if bit % 2 else "R0") mirror="MX" if bit % 2 else "R0")
def add_end_caps(self): def add_end_caps(self):
@ -386,12 +395,12 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
# FIXME: These depend on the array size itself # FIXME: These depend on the array size itself
# Far bottom dummy row (first row below array IS flipped) # Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.rbl[0] + 1) % 2 flip_dummy = (self.rbl[0] + 1) % 2
dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset
self.dummy_row_insts[0].place(offset=dummy_row_offset, self.dummy_row_insts[0].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0") mirror="MX" if flip_dummy else "R0")
# Far left dummy col # Far left dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + self.unused_offset
self.dummy_col_insts[0].place(offset=dummy_col_offset) self.dummy_col_insts[0].place(offset=dummy_col_offset)
# Far right dummy col # Far right dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array # Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
@ -495,13 +504,13 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
def ground_pin(self, inst, name): def ground_pin(self, inst, name):
pin = inst.get_pin(name) pin = inst.get_pin(name)
pin_layer = pin.layer pin_layer = pin.layer
layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy())
right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy())
# Place the pins a track outside of the array # Place the pins a track outside of the array
left_loc = left_pin_loc - vector(layer_pitch, 0) left_loc = left_pin_loc - vector(self.unused_pitch, 0)
right_loc = right_pin_loc + vector(layer_pitch, 0) right_loc = right_pin_loc + vector(self.unused_pitch, 0)
self.add_power_pin("gnd", left_loc, directions=("H", "H")) self.add_power_pin("gnd", left_loc, directions=("H", "H"))
self.add_power_pin("gnd", right_loc, directions=("H", "H")) self.add_power_pin("gnd", right_loc, directions=("H", "H"))

View File

@ -109,12 +109,13 @@ class wordline_buffer_array(design.design):
def place_drivers(self): def place_drivers(self):
for row in range(self.rows): for row in range(self.rows):
# These are flipped since we always start with an RBL on the bottom
if (row % 2): if (row % 2):
y_offset = self.wl_driver.height * (row + 1)
inst_mirror = "MX"
else:
y_offset = self.wl_driver.height * row y_offset = self.wl_driver.height * row
inst_mirror = "R0" inst_mirror = "R0"
else:
y_offset = self.wl_driver.height * (row + 1)
inst_mirror = "MX"
offset = [0, y_offset] offset = [0, y_offset]

View File

@ -44,7 +44,7 @@ class wordline_driver_array(design.design):
self.place_drivers() self.place_drivers()
self.route_layout() self.route_layout()
self.route_vdd_gnd() self.route_vdd_gnd()
self.offset_all_coordinates() self.offset_x_coordinates()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -60,8 +60,10 @@ class wordline_driver_array(design.design):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): def add_modules(self):
self.wl_driver = factory.create(module_type="wordline_driver", self.wl_driver = factory.create(module_type="wordline_driver",
size=self.cols) cols=self.cols)
self.add_mod(self.wl_driver) self.add_mod(self.wl_driver)
def route_vdd_gnd(self): def route_vdd_gnd(self):

View File

@ -30,6 +30,9 @@ class options(optparse.Values):
num_r_ports = 0 num_r_ports = 0
num_w_ports = 0 num_w_ports = 0
# By default, use local arrays with a max fanout of 16
#local_array_size = 16
# Write mask size, default will be overwritten with word_size if not user specified # Write mask size, default will be overwritten with word_size if not user specified
write_size = None write_size = None
@ -139,6 +142,7 @@ class options(optparse.Values):
bank_select = "bank_select" bank_select = "bank_select"
bitcell_array = "bitcell_array" bitcell_array = "bitcell_array"
bitcell = "bitcell" bitcell = "bitcell"
buf_dec = "pbuf"
column_mux_array = "single_level_column_mux_array" column_mux_array = "single_level_column_mux_array"
control_logic = "control_logic" control_logic = "control_logic"
decoder = "hierarchical_decoder" decoder = "hierarchical_decoder"

View File

@ -96,4 +96,4 @@ class pbuf(pgate.pgate):
offset=a_pin.center(), offset=a_pin.center(),
width=a_pin.width(), width=a_pin.width(),
height=a_pin.height()) height=a_pin.height())

View File

@ -18,9 +18,9 @@ class wordline_driver(design.design):
This is an AND (or NAND) with configurable drive strength to drive the wordlines. This is an AND (or NAND) with configurable drive strength to drive the wordlines.
It is matched to the bitcell height. It is matched to the bitcell height.
""" """
def __init__(self, name, size=1, height=None): def __init__(self, name, cols, height=None):
debug.info(1, "Creating wordline_driver {}".format(name)) debug.info(1, "Creating wordline_driver {}".format(name))
self.add_comment("size: {}".format(size)) self.add_comment("cols: {}".format(cols))
super().__init__(name) super().__init__(name)
if height is None: if height is None:
@ -28,7 +28,7 @@ class wordline_driver(design.design):
self.height = b.height self.height = b.height
else: else:
self.height = height self.height = height
self.size = size self.cols = cols
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -42,10 +42,25 @@ class wordline_driver(design.design):
def create_modules(self): def create_modules(self):
self.nand = factory.create(module_type="nand2_dec", self.nand = factory.create(module_type="nand2_dec",
height=self.height) height=self.height)
self.driver = factory.create(module_type="inv_dec", try:
size=self.size, local_array_size = OPTS.local_array_size
height=self.nand.height) driver_size = max(int(self.cols / local_array_size), 1)
except AttributeError:
local_array_size = 0
# Defautl to FO4
driver_size = max(int(self.cols / 4), 1)
# The polarity must be switched if we have a hierarchical wordline
# to compensate for the local array inverters
if local_array_size > 0:
self.driver = factory.create(module_type="buf_dec",
size=driver_size,
height=self.nand.height)
else:
self.driver = factory.create(module_type="inv_dec",
size=driver_size,
height=self.nand.height)
self.add_mod(self.nand) self.add_mod(self.nand)
self.add_mod(self.driver) self.add_mod(self.driver)

View File

@ -25,7 +25,7 @@ class wordline_driver_test(openram_test):
# check wordline driver for single port # check wordline driver for single port
debug.info(2, "Checking driver") debug.info(2, "Checking driver")
tx = factory.create(module_type="wordline_driver") tx = factory.create(module_type="wordline_driver", cols=8)
self.local_check(tx) self.local_check(tx)
globals.end_openram() globals.end_openram()

View File

@ -27,11 +27,11 @@ class port_address_1rw_1r_test(openram_test):
globals.setup_bitcell() globals.setup_bitcell()
debug.info(1, "Port address 16 rows") debug.info(1, "Port address 16 rows")
a = factory.create("port_address", cols=16, rows=16) a = factory.create("port_address", cols=16, rows=16, port=0)
self.local_check(a) self.local_check(a)
debug.info(1, "Port address 256 rows") debug.info(1, "Port address 256 rows")
a = factory.create("port_address", cols=256, rows=256) a = factory.create("port_address", cols=256, rows=256, port=1)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -21,11 +21,11 @@ class port_address_test(openram_test):
globals.init_openram(config_file) globals.init_openram(config_file)
debug.info(1, "Port address 16 rows") debug.info(1, "Port address 16 rows")
a = factory.create("port_address", cols=16, rows=16) a = factory.create("port_address", cols=16, rows=16, port=0)
self.local_check(a) self.local_check(a)
debug.info(1, "Port address 512 rows") debug.info(1, "Port address 512 rows")
a = factory.create("port_address", cols=256, rows=512) a = factory.create("port_address", cols=256, rows=512, port=0)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()