diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index 586ec51d..8e39094b 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -29,6 +29,9 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): self.add_rbl = rbl else: self.add_rbl = add_rbl + + debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.") + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -68,66 +71,96 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): self.add_mod(self.bitcell_array) self.wl_array = factory.create(module_type="wordline_buffer_array", - rows=self.rows + len(self.all_ports), + rows=self.rows + 1, cols=self.cols) self.add_mod(self.wl_array) def add_pins(self): self.bitline_names = self.bitcell_array.get_all_bitline_names() + + self.driver_wordline_inputs = [] + self.driver_wordline_outputs = [] + self.array_wordline_inputs = [] - self.driver_wordline_inputs = [x for x in self.bitcell_array.get_all_wordline_names() if not x.startswith("dummy")] - self.driver_wordline_outputs = [x + "i" for x in self.driver_wordline_inputs] - self.array_wordline_inputs = [x + "i" if not x.startswith("dummy") else "gnd" for x in self.bitcell_array.get_all_wordline_names()] + # Port 0 + wordline_inputs = [x for x in self.bitcell_array.get_wordline_names(0) if not x.startswith("dummy")] + if len(self.all_ports) > 1: + # Drop off the RBL for port 1 + self.driver_wordline_inputs.append(wordline_inputs[:-1]) + else: + self.driver_wordline_inputs.append(wordline_inputs) + self.driver_wordline_outputs.append([x + "i" for x in self.driver_wordline_inputs[-1]]) + self.array_wordline_inputs.append([x + "i" if not x.startswith("dummy") else "gnd" for x in self.bitcell_array.get_wordline_names(0)]) + + # Port 1 + if len(self.all_ports) > 1: + self.driver_wordline_inputs.append([x for x in self.bitcell_array.get_wordline_names(1) if not x.startswith("dummy")][1:]) + self.driver_wordline_outputs.append([x + "i" for x in self.driver_wordline_inputs[-1]]) + self.array_wordline_inputs.append([x + "i" if not x.startswith("dummy") else "gnd" for x in self.bitcell_array.get_wordline_names(1)]) + + self.all_driver_wordline_inputs = [x for x in self.bitcell_array.get_wordline_names() if not x.startswith("dummy")] self.replica_names = self.bitcell_array.get_rbl_wordline_names() + self.gnd_wl_names = [] + + # Connect unused RBL WL to gnd + array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")]) + dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")]) + rbl_wl_names = set([self.bitcell_array.get_rbl_wordline_names(x) for x in self.all_ports]) + self.gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names) + + self.all_array_wordline_inputs = [x + "i" if x not in self.gnd_wl_names else "gnd" for x in self.bitcell_array.get_wordline_names()] + + self.bitline_names = self.bitcell_array.get_inouts() # Arrays are always: # word lines (bottom to top) # bit lines (left to right) # vdd # gnd - self.add_pin_list(self.driver_wordline_inputs, "INPUT") + for port in self.all_ports: + self.add_pin_list(self.all_driver_wordline_inputs, "INPUT") self.add_pin_list(self.bitline_names, "INOUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") def create_instances(self): """ Create the module instances used in this design """ - - self.wl_inst = self.add_inst(name="wl_driver", - mod=self.wl_array) - self.connect_inst(self.driver_wordline_inputs + self.driver_wordline_outputs + ["vdd", "gnd"]) + + self.wl_insts = [] + for port in self.all_ports: + self.wl_insts.append(self.add_inst(name="wl_driver", + mod=self.wl_array)) + self.connect_inst(self.driver_wordline_inputs[port] + self.driver_wordline_outputs[port] + ["vdd", "gnd"]) self.bitcell_array_inst = self.add_inst(name="array", - mod=self.bitcell_array, - offset=self.wl_inst.lr()) - self.connect_inst(self.array_wordline_inputs + self.bitline_names + ["vdd", "gnd"]) + mod=self.bitcell_array) + + self.connect_inst(self.all_array_wordline_inputs + self.bitline_names + ["vdd", "gnd"]) def place(self): """ Place the bitcelll array to the right of the wl driver. """ - - self.wl_inst.place(vector(0, self.cell.height)) # FIXME: Replace this with a tech specific paramter driver_to_array_spacing = 3 * self.m3_pitch - self.bitcell_array_inst.place(vector(self.wl_inst.rx() + driver_to_array_spacing, + + self.wl_insts[0].place(vector(0, self.cell.height)) + + self.bitcell_array_inst.place(vector(self.wl_insts[0].rx() + driver_to_array_spacing, 0)) + if len(self.all_ports) > 1: + self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing, + 2 * self.cell.height), + mirror="MY") + self.height = self.bitcell_array.height self.width = self.bitcell_array_inst.rx() def route_unused_wordlines(self): """ Connect the unused RBL and dummy wordlines to gnd """ - gnd_wl_names = [] - # Connect unused RBL WL to gnd - array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")]) - dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")]) - rbl_wl_names = set([self.bitcell_array.get_rbl_wordline_names(x) for x in self.all_ports]) - - gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names) - - for wl_name in gnd_wl_names: + for wl_name in self.gnd_wl_names: pin = self.bitcell_array_inst.get_pin(wl_name) pin_layer = pin.layer layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) @@ -149,10 +182,11 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): for (x, y) in zip(self.bitline_names, self.bitcell_array.get_inouts()): self.copy_layout_pin(self.bitcell_array_inst, y, x) - for (x, y) in zip(self.driver_wordline_inputs, self.wl_array.get_inputs()): - self.copy_layout_pin(self.wl_inst, y, x) + for port in self.all_ports: + for (x, y) in zip(self.driver_wordline_inputs[port], self.wl_array.get_inputs()): + self.copy_layout_pin(self.wl_insts[port], y, x) - supply_insts = [self.wl_inst, self.bitcell_array_inst] + supply_insts = [*self.wl_insts, self.bitcell_array_inst] for pin_name in ["vdd", "gnd"]: for inst in supply_insts: pin_list = inst.get_pins(pin_name) @@ -162,12 +196,28 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): start_layer=pin.layer) def route(self): - array_names = [x for x in self.bitcell_array.get_all_wordline_names() if not x.startswith("dummy")] - for (driver_name, array_name) in zip(self.wl_array.get_outputs(), array_names): - out_pin = self.wl_inst.get_pin(driver_name) - in_pin = self.bitcell_array_inst.get_pin(array_name) - mid_loc = self.wl_inst.rx() + 1.5 * self.m3_pitch - self.add_path(out_pin.layer, [out_pin.rc(), vector(mid_loc, out_pin.cy()), in_pin.lc()]) + + for port in self.all_ports: + if port == 0: + array_names = [x for x in self.bitcell_array.get_wordline_names(port) if not x.startswith("dummy")] + if len(self.all_ports) > 1: + # Drop off the RBL for port 1 + array_names = array_names[:-1] + else: + array_names = [x for x in self.bitcell_array.get_wordline_names(port) if not x.startswith("dummy")][1:] + + for (driver_name, array_name) in zip(self.wl_array.get_outputs(), array_names): + out_pin = self.wl_insts[port].get_pin(driver_name) + in_pin = self.bitcell_array_inst.get_pin(array_name) + if port == 0: + out_loc = out_pin.rc() + mid_loc = vector(self.wl_insts[port].rx() + 1.5 * self.m3_pitch, out_loc.y) + in_loc = in_pin.lc() + else: + out_loc = out_pin.lc() + mid_loc = vector(self.wl_insts[port].lx() - 1.5 * self.m3_pitch, out_loc.y) + in_loc = in_pin.rc() + self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc]) self.route_unused_wordlines() diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index b99c741b..f4214175 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -218,8 +218,10 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): self.add_pin_list(self.bitline_names, "INOUT") def add_wordline_pins(self): - - # All wordline names for all ports + + # Regular wordline names + self.main_wordline_names = [] + # Create the full WL names include dummy, replica, and regular bit cells self.wordline_names = [] # Wordline names for each port self.wordline_names_by_port = [[] for x in self.all_ports] @@ -234,9 +236,6 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): # These are the non-indexed names dummy_cell_wl_names = ["dummy_" + x for x in self.cell.get_all_wl_names()] - # Create the full WL names include dummy, replica, and regular bit cells - self.wordline_names = [] - self.dummy_wordline_names["bot"] = ["{0}_bot".format(x) for x in dummy_cell_wl_names] self.wordline_names.extend(self.dummy_wordline_names["bot"]) @@ -250,6 +249,7 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): # Regular WLs self.wordline_names.extend(self.bitcell_array_wordline_names) + self.main_wordline_names = self.bitcell_array_wordline_names # Right port WLs for port in range(self.left_rbl, self.left_rbl + self.right_rbl): @@ -490,9 +490,14 @@ class replica_bitcell_array(bitcell_base_array.bitcell_base_array): bl_names = self.replica_bitline_names[port] return bl_names[2 * port:2 * port + 2] - def get_wordline_names(self): + def get_wordline_names(self, port=None): """ Return the wordline names """ - return self.wordline_names + if port == None: + return self.wordline_names + else: + wl_name = self.cell.get_all_wl_names()[port] + temp = [x for x in self.wordline_names if wl_name in x] + return temp def get_bitline_names(self): """ Return the bitline names """