From b841fd7ce3fdb74f6b2a9c98e56decaf5e98b8ff Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jul 2019 15:56:51 -0700 Subject: [PATCH] Replica bitcell array with arbitrary RBLs working --- compiler/bitcells/dummy_pbitcell.py | 91 ++++ compiler/bitcells/pbitcell.py | 15 +- compiler/modules/bank.py | 12 +- compiler/modules/dummy_array.py | 5 +- compiler/modules/port_data.py | 90 ++-- compiler/modules/replica_bitcell_array.py | 298 ++++++++------ compiler/modules/replica_bitline.py | 387 +----------------- compiler/modules/replica_column.py | 69 ++-- compiler/sram/sram_base.py | 3 +- compiler/tests/04_dummy_pbitcell_test.py | 50 +++ .../tests/05_bitcell_1rw_1r_array_test.py | 4 +- .../05_replica_bitcell_1rw_1r_array_test.py | 36 ++ .../tests/05_replica_bitcell_array_test.py | 9 +- compiler/tests/05_replica_column_test.py | 8 +- .../tests/05_replica_pbitcell_array_test.py | 40 ++ compiler/tests/19_single_bank_1rw_1r_test.py | 13 +- compiler/tests/19_single_bank_1w_1r_test.py | 17 +- compiler/tests/testutils.py | 2 + technology/scn4m_subm/mag_lib/setup.tcl | 12 +- 19 files changed, 565 insertions(+), 596 deletions(-) create mode 100644 compiler/bitcells/dummy_pbitcell.py create mode 100755 compiler/tests/04_dummy_pbitcell_test.py create mode 100755 compiler/tests/05_replica_bitcell_1rw_1r_array_test.py create mode 100755 compiler/tests/05_replica_pbitcell_array_test.py diff --git a/compiler/bitcells/dummy_pbitcell.py b/compiler/bitcells/dummy_pbitcell.py new file mode 100644 index 00000000..ee15e03c --- /dev/null +++ b/compiler/bitcells/dummy_pbitcell.py @@ -0,0 +1,91 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +import design +from tech import drc, spice,parameter +from vector import vector +from globals import OPTS +from sram_factory import factory + +class dummy_pbitcell(design.design): + """ + Creates a replica bitcell using pbitcell + """ + + def __init__(self, name): + self.num_rw_ports = OPTS.num_rw_ports + self.num_w_ports = OPTS.num_w_ports + self.num_r_ports = OPTS.num_r_ports + self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports + + design.design.__init__(self, name) + debug.info(1, "create a dummy bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, + self.num_w_ports, + self.num_r_ports)) + + self.create_netlist() + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_modules() + + def create_layout(self): + self.place_pbitcell() + self.route_rbc_connections() + self.DRC_LVS() + + def add_pins(self): + for port in range(self.total_ports): + self.add_pin("bl{}".format(port)) + self.add_pin("br{}".format(port)) + + for port in range(self.total_ports): + self.add_pin("wl{}".format(port)) + + self.add_pin("vdd") + self.add_pin("gnd") + + def add_modules(self): + self.prbc = factory.create(module_type="pbitcell",dummy_bitcell=True) + self.add_mod(self.prbc) + + self.height = self.prbc.height + self.width = self.prbc.width + + def create_modules(self): + self.prbc_inst = self.add_inst(name="pbitcell", + mod=self.prbc) + + temp = [] + for port in range(self.total_ports): + temp.append("bl{}".format(port)) + temp.append("br{}".format(port)) + for port in range(self.total_ports): + temp.append("wl{}".format(port)) + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) + + def place_pbitcell(self): + self.prbc_inst.place(offset=vector(0,0)) + + def route_rbc_connections(self): + for port in range(self.total_ports): + self.copy_layout_pin(self.prbc_inst, "bl{}".format(port)) + self.copy_layout_pin(self.prbc_inst, "br{}".format(port)) + for port in range(self.total_ports): + self.copy_layout_pin(self.prbc_inst, "wl{}".format(port)) + self.copy_layout_pin(self.prbc_inst, "vdd") + self.copy_layout_pin(self.prbc_inst, "gnd") + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This module is made using a pbitcell. Get the cin from that module + return self.prbc.get_wl_cin() diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index eb44a27a..63691233 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -20,13 +20,14 @@ class pbitcell(design.design): with a variable number of read/write, write, and read ports """ - def __init__(self, name, replica_bitcell=False): + def __init__(self, name, replica_bitcell=False, dummy_bitcell=False): self.num_rw_ports = OPTS.num_rw_ports self.num_w_ports = OPTS.num_w_ports self.num_r_ports = OPTS.num_r_ports self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports self.replica_bitcell = replica_bitcell + self.dummy_bitcell = dummy_bitcell design.design.__init__(self, name) debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, @@ -686,8 +687,10 @@ class pbitcell(design.design): port_contact_offest = left_port_transistors[k].get_pin("S").center() bl_offset = vector(bl_positions[k].x, port_contact_offest.y) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=port_contact_offest) + # Leave bitline disconnected if a dummy cell + if not self.dummy_bitcell: + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offest) self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height) @@ -695,8 +698,10 @@ class pbitcell(design.design): port_contact_offest = right_port_transistors[k].get_pin("D").center() br_offset = vector(br_positions[k].x, port_contact_offest.y) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=port_contact_offest) + # Leave bitline disconnected if a dummy cell + if not self.dummy_bitcell: + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=port_contact_offest) self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 45d860b2..ebf25236 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -156,7 +156,9 @@ class bank(design.design): # These are the offsets of the main array (excluding dummy and replica rows/cols) self.main_bitcell_array_top = self.bitcell_array_top - (self.num_ports*self.bitcell.height) - self.main_bitcell_array_left = 2*self.bitcell.width + # Just past the dummy column + self.main_bitcell_array_left = self.bitcell.width + # Just past the dummy row and replica row self.main_bitcell_array_bottom = 2*self.bitcell.height self.compute_instance_port0_offsets() @@ -362,9 +364,8 @@ class bank(design.design): for row in range(self.num_rows): for wordline in self.wl_names: temp.append(wordline+"_{0}".format(row)) - for row in range(self.num_ports): - for wordline in self.wl_names: - temp.append(wordline+"_{0}".format(row)) + for wordline in self.wl_names: + temp.append("replica_"+wordline) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) @@ -384,6 +385,9 @@ class bank(design.design): mod=self.port_data[port]) temp = [] + if port in self.read_ports: + temp.append("rbl_{0}".format(self.bl_names[port])) + temp.append("rbl_{0}".format(self.br_names[port])) 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)) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 0790367f..861e0fad 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -15,13 +15,14 @@ class dummy_array(design.design): """ Generate a dummy row/column for the replica array. """ - def __init__(self, cols, rows, name): + def __init__(self, cols, rows, mirror, name): design.design.__init__(self, name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.column_size = cols self.row_size = rows + self.mirror = mirror self.create_netlist() if not OPTS.netlist_only: @@ -46,7 +47,7 @@ class dummy_array(design.design): for row in range(self.row_size): name = "dummy_r{0}_c{1}".format(row, col) - if row % 2: + if (row+self.mirror) % 2: tempy = yoffset + self.dummy_cell.height dir_key = "MX" else: diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index ab594c09..4dce8d59 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -71,6 +71,9 @@ class port_data(design.design): def add_pins(self): """ Adding pins for port address module""" + if self.port in self.read_ports or self.col_addr_size>0: + self.add_pin("rbl_"+self.bl_names[self.port],"INOUT") + self.add_pin("rbl_"+self.br_names[self.port],"INOUT") for bit in range(self.num_cols): self.add_pin(self.bl_names[self.port]+"_{0}".format(bit),"INOUT") self.add_pin(self.br_names[self.port]+"_{0}".format(bit),"INOUT") @@ -134,8 +137,9 @@ class port_data(design.design): def add_modules(self): if self.port in self.read_ports: + # Extra column for RBL self.precharge_array = factory.create(module_type="precharge_array", - columns=self.num_cols, + columns=self.num_cols + 1, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) self.add_mod(self.precharge_array) @@ -144,9 +148,19 @@ class port_data(design.design): word_size=self.word_size, words_per_row=self.words_per_row) self.add_mod(self.sense_amp_array) + elif self.col_addr_size>0: + # Precharge is needed when we have a column mux + self.precharge_array = factory.create(module_type="precharge_array", + columns=self.num_cols, + bitcell_bl=self.bl_names[self.port], + bitcell_br=self.br_names[self.port]) + self.add_mod(self.precharge_array) + self.sense_amp_array = None + else: self.precharge_array = None self.sense_amp_array = None + if self.col_addr_size > 0: self.column_mux_array = factory.create(module_type="column_mux_array", @@ -197,7 +211,10 @@ class port_data(design.design): self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port), mod=self.precharge_array) + temp = [] + temp.append("rbl_"+self.bl_names[self.port]) + temp.append("rbl_"+self.br_names[self.port]) for bit in range(self.num_cols): temp.append(self.bl_names[self.port]+"_{0}".format(bit)) temp.append(self.br_names[self.port]+"_{0}".format(bit)) @@ -296,21 +313,30 @@ class port_data(design.design): vertical_port_order.append(self.sense_amp_array_inst) vertical_port_order.append(self.write_driver_array_inst) + # Add one column for the the RBL + if self.port in self.read_ports: + x_offset = self.bitcell.width + else: + x_offset = 0 + vertical_port_offsets = 4*[None] - self.width = 0 + self.width = x_offset self.height = 0 for i,p in enumerate(vertical_port_order): if p==None: continue self.height += (p.height + self.m2_gap) self.width = max(self.width, p.width) - vertical_port_offsets[i]=vector(0,self.height) + vertical_port_offsets[i]=vector(x_offset,self.height) # Reversed order self.write_driver_offset = vertical_port_offsets[3] self.sense_amp_offset = vertical_port_offsets[2] self.column_mux_offset = vertical_port_offsets[1] self.precharge_offset = vertical_port_offsets[0] + if self.precharge_offset: + self.precharge_offset -= vector(x_offset,0) + @@ -356,14 +382,15 @@ class port_data(design.design): inst1 = self.column_mux_array_inst inst2 = self.precharge_array_inst - self.connect_bitlines(inst1, inst2, self.num_cols) - + self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1) + 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 """ inst2 = self.sense_amp_array_inst - + + start_bit = 0 if self.col_addr_size>0: # Sense amp is connected to the col mux inst1 = self.column_mux_array_inst @@ -374,9 +401,11 @@ class port_data(design.design): inst1 = self.precharge_array_inst inst1_bl_name = "bl_{}" inst1_br_name = "br_{}" - + start_bit=1 + + 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_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit) def route_write_driver_to_column_mux_or_bitcell_array(self, port): """ Routing of BL and BR between sense_amp and column mux or bitcell array """ @@ -408,10 +437,14 @@ class port_data(design.design): def route_bitline_pins(self): """ Add the bitline pins for the given port """ + if self.port in self.read_ports: + self.copy_layout_pin(self.precharge_array_inst, "bl_0", "rbl_bl") + self.copy_layout_pin(self.precharge_array_inst, "br_0", "rbl_br") + for bit in range(self.num_cols): if self.port in self.read_ports: - self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit)) - self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit)) + self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+1), "bl_{}".format(bit)) + self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+1), "br_{}".format(bit)) elif self.column_mux_array_inst: self.copy_layout_pin(self.column_mux_array_inst, "bl_{}".format(bit)) self.copy_layout_pin(self.column_mux_array_inst, "br_{}".format(bit)) @@ -434,8 +467,8 @@ class port_data(design.design): def channel_route_bitlines(self, inst1, inst2, num_bits, - inst1_bl_name="bl_{}", inst1_br_name="br_{}", - inst2_bl_name="bl_{}", inst2_br_name="br_{}"): + inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0, + inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0): """ Route the bl and br of two modules using the channel router. """ @@ -443,26 +476,27 @@ class port_data(design.design): # determine top and bottom automatically. # since they don't overlap, we can just check the bottom y coordinate. if inst1.by() < inst2.by(): - (bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name) - (top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name) + (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) = (inst2, inst2_bl_name, inst2_br_name) - (top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name) + (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) # 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) 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))] + 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))] route_map = list(zip(bottom_names, top_names)) self.create_horizontal_channel_route(route_map, offset) def connect_bitlines(self, inst1, inst2, num_bits, - inst1_bl_name="bl_{}", inst1_br_name="br_{}", - inst2_bl_name="bl_{}", inst2_br_name="br_{}"): + inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0, + inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0): + """ Connect the bl and br of two modules. This assumes that they have sufficient space to create a jog @@ -472,17 +506,17 @@ class port_data(design.design): # determine top and bottom automatically. # since they don't overlap, we can just check the bottom y coordinate. if inst1.by() < inst2.by(): - (bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name) - (top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name) + (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) = (inst2, inst2_bl_name, inst2_br_name) - (top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name) + (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) for col in range(num_bits): - bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc() - bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc() - top_bl = top_inst.get_pin(top_bl_name.format(col)).bc() - top_br = top_inst.get_pin(top_br_name.format(col)).bc() + 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() yoffset = 0.5*(top_bl.y+bottom_bl.y) self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset), diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 4596f94d..29724125 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -22,18 +22,25 @@ class replica_bitcell_array(design.design): Dummy are the outside columns/rows with WL and BL tied to gnd. Requires a regular bitcell array, replica bitcell, and dummy bitcell (Bl/BR disconnected). """ - def __init__(self, cols, rows, num_ports, name): + def __init__(self, cols, rows, left_rbl, right_rbl, name): design.design.__init__(self, name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.column_size = cols self.row_size = rows - self.num_ports = num_ports + self.left_rbl = left_rbl + self.right_rbl = right_rbl + # FIXME: If we want more than 2 ports of RBL, we also need to modify + # replica_column to support this. Right now, it only supports a single + # RBL and is used for both the left and right column (right is flipped). + #debug.check(self.left_rbl<=1,"Only one RBL supported now.") + #debug.check(self.right_rbl<=1,"Only one RBL supported now.") + # Two dummy rows/cols plus replica for each port - self.extra_rows = 2 + self.num_ports - self.extra_cols = 2 + self.num_ports + self.extra_rows = 2 + left_rbl + right_rbl + self.extra_cols = 2 + left_rbl + right_rbl self.create_netlist() if not OPTS.netlist_only: @@ -83,22 +90,32 @@ class replica_bitcell_array(design.design): rows=self.row_size) self.add_mod(self.bitcell_array) - # Replica bitline - self.replica_column = factory.create(module_type="replica_column", - rows=self.row_size + self.extra_rows, - num_ports = self.num_ports) - self.add_mod(self.replica_column) + # Replica bitlines + self.replica_columns = {} + for bit in range(self.left_rbl+self.right_rbl): + if bit1 port) + for bit in range(self.left_rbl): + self.replica_col_wl_names.extend(["replica_{0}_{1}".format(x,bit) for x in self.cell.list_all_wl_names()]) + # Regular WLs + self.replica_col_wl_names.extend(self.wl_names) + # Right port WLs (one dummy for each port when we allow >1 port) + for bit in range(self.left_rbl,self.left_rbl+self.right_rbl): + self.replica_col_wl_names.extend(["replica_{0}_{1}".format(x,bit) for x in self.cell.list_all_wl_names()]) + self.replica_col_wl_names.extend(["{0}_top".format(x) for x in self.dummy_cell_wl_names]) + + # Left/right dummy columns are connected identically to the replica column self.dummy_col_wl_names = self.replica_col_wl_names - - + + + # Per bit bitline names + self.replica_bl_names_list = {} + self.replica_wl_names_list = {} + # Array of all bitline names + self.replica_bl_names = [] + self.replica_wl_names = [] + for bit in range(self.left_rbl+self.right_rbl): + self.replica_bl_names_list[bit] = ["replica_{0}_{1}".format(x,bit) for x in self.cell.list_all_bitline_names()] + self.replica_bl_names.extend(self.replica_bl_names_list[bit]) + + self.replica_wl_names_list[bit] = ["{0}_{1}".format(x,bit) for x in self.replica_cell_wl_names] + self.replica_wl_names.extend(self.replica_wl_names_list[bit]) + + self.add_pin_list(self.bl_names) - self.add_pin_list(self.replica_bl0_names) - if self.num_ports==2: - self.add_pin_list(self.replica_bl1_names) + self.add_pin_list(self.replica_bl_names) self.add_pin_list(self.wl_names) self.add_pin_list(self.replica_wl_names) - #self.add_pin_list([x for x in self.replica_col_wl_names if not x.startswith("dummy")]) - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def create_instances(self): """ Create the module instances used in this design """ supplies = ["vdd", "gnd"] + + # Used for names/dimensions only + self.cell = factory.create(module_type="bitcell") + # Main array self.bitcell_array_inst=self.add_inst(name="bitcell_array", mod=self.bitcell_array) self.connect_inst(self.bitcell_array.pins) - # Replica columns (two even if one port for now) - self.replica_col_left_inst=self.add_inst(name="replica_col_left", - mod=self.replica_column) - self.connect_inst(self.replica_bl0_names + self.replica_col_wl_names + supplies) + # Replica columns + self.replica_col_inst = {} + for bit in range(self.left_rbl): + self.replica_col_inst[bit]=self.add_inst(name="replica_col_left_{}".format(bit), + mod=self.replica_columns[bit]) + self.connect_inst(self.replica_bl_names_list[bit] + self.replica_col_wl_names + supplies) - if self.num_ports==2: - self.replica_col_right_inst=self.add_inst(name="replica_col_right", - mod=self.replica_column) - self.connect_inst(self.replica_bl1_names + self.replica_col_wl_names[::-1] + supplies) + for bit in range(self.left_rbl,self.left_rbl+self.right_rbl): + self.replica_col_inst[bit]=self.add_inst(name="replica_col_right_{}".format(bit), + mod=self.replica_columns[bit]) + self.connect_inst(self.replica_bl_names_list[bit] + self.replica_col_wl_names + supplies) + # Replica rows with replica bitcell - self.dummy_row_bottop_inst=self.add_inst(name="dummy_row_bottop", - mod=self.dummy_row) - self.connect_inst(self.dummy_row_bl_names + [x+"_0" for x in self.replica_wl_names] + supplies) - self.dummy_row_topbot_inst=self.add_inst(name="dummy_row_topbot", - mod=self.dummy_row) - self.connect_inst(self.dummy_row_bl_names + [x+"_1" for x in self.replica_wl_names] + supplies) + self.dummy_row_replica_inst = {} + for bit in range(self.left_rbl+self.right_rbl): + self.dummy_row_replica_inst[bit]=self.add_inst(name="dummy_row_{}".format(bit), + mod=self.dummy_row) + self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names_list[bit] + supplies) - # Dummy rows without replica bitcell - self.dummy_row_botbot_inst=self.add_inst(name="dummy_row_botbot", + # Top/bottom dummy rows + self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot", mod=self.dummy_row) - self.connect_inst(self.dummy_row_bl_names + [x+"_0" for x in self.dummy_wl_names] + supplies) - self.dummy_row_toptop_inst=self.add_inst(name="dummy_row_toptop", + self.connect_inst(self.dummy_row_bl_names + [x+"_bot" for x in self.dummy_cell_wl_names] + supplies) + self.dummy_row_top_inst=self.add_inst(name="dummy_row_top", mod=self.dummy_row) - self.connect_inst(self.dummy_row_bl_names + [x+"_1" for x in self.dummy_wl_names] + supplies) + self.connect_inst(self.dummy_row_bl_names + [x+"_top" for x in self.dummy_cell_wl_names] + supplies) - # Dummy columns + # Left/right Dummy columns self.dummy_col_left_inst=self.add_inst(name="dummy_col_left", mod=self.dummy_col) - self.connect_inst([x+"_0" for x in self.dummy_bl_names] + self.dummy_col_wl_names + supplies) + self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) self.dummy_col_right_inst=self.add_inst(name="dummy_col_right", mod=self.dummy_col) - self.connect_inst([x+"_1" for x in self.dummy_bl_names] + self.dummy_col_wl_names + supplies) + self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) def create_layout(self): self.height = (self.row_size+self.extra_rows)*self.dummy_row.height - self.width = (self.column_size+self.extra_cols)*self.replica_column.width + self.width = (self.column_size+self.extra_cols)*self.cell.width # This is a bitcell x bitcell offset to scale - offset = vector(self.replica_column.width, self.dummy_row.height) + offset = vector(self.cell.width, self.cell.height) self.bitcell_array_inst.place(offset=[0,0]) - self.replica_col_left_inst.place(offset=offset.scale(-1,-2)) - if self.num_ports==2: - self.replica_col_right_inst.place(offset=offset.scale(0,2)+self.bitcell_array_inst.ur(), - mirror="MX") - - self.dummy_row_toptop_inst.place(offset=offset.scale(0,2)+self.bitcell_array_inst.ul(), - mirror="MX") - self.dummy_row_topbot_inst.place(offset=offset.scale(0,0)+self.bitcell_array_inst.ul()) - self.dummy_row_bottop_inst.place(offset=offset.scale(0,0), - mirror="MX") - self.dummy_row_botbot_inst.place(offset=offset.scale(0,-2)) - self.dummy_col_left_inst.place(offset=offset.scale(-2,-2)) - if self.num_ports==2: - self.dummy_col_right_inst.place(offset=offset.scale(1,-2)+self.bitcell_array_inst.lr()) - else: - self.dummy_col_right_inst.place(offset=offset.scale(0,-2)+self.bitcell_array_inst.lr()) + # To the left of the bitcell array + for bit in range(self.left_rbl): + self.replica_col_inst[bit].place(offset=offset.scale(-bit-1,-self.left_rbl-1)) + # To the right of the bitcell array + for bit in range(self.right_rbl): + self.replica_col_inst[self.left_rbl+bit].place(offset=offset.scale(bit,-self.left_rbl-1)+self.bitcell_array_inst.lr()) + + + # Far top dummy row (first row above array is NOT flipped) + flip_dummy = self.right_rbl%2 + self.dummy_row_top_inst.place(offset=offset.scale(0,self.right_rbl+flip_dummy)+self.bitcell_array_inst.ul(), + mirror="MX" if flip_dummy else "R0") + # Far bottom dummy row (first row below array IS flipped) + flip_dummy = (self.left_rbl+1)%2 + self.dummy_row_bot_inst.place(offset=offset.scale(0,-self.left_rbl-1+flip_dummy), + mirror="MX" if flip_dummy else "R0") + # Far left dummy col + self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl-1,-self.left_rbl-1)) + # Far right dummy col + self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl,-self.left_rbl-1)+self.bitcell_array_inst.lr()) + + # Replica dummy rows + for bit in range(self.left_rbl): + self.dummy_row_replica_inst[bit].place(offset=offset.scale(0,-bit-bit%2), + mirror="R0" if bit%2 else "MX") + for bit in range(self.right_rbl): + self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2)+self.bitcell_array_inst.ul(), + mirror="MX" if bit%2 else "R0") - self.translate_all(offset.scale(-2,-2)) + self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl)) self.add_layout_pins() @@ -234,52 +282,50 @@ class replica_bitcell_array(design.design): if pin_name.startswith("wl"): pin_list = self.bitcell_array_inst.get_pins(pin_name) for pin in pin_list: - self.add_layout_pin_rect_center(text=pin_name, - layer=pin.layer, - offset=pin.center(), - width=self.width, - height=pin.height()) + self.add_layout_pin(text=pin_name, + layer=pin.layer, + offset=pin.ll().scale(0,1), + width=self.width, + height=pin.height()) elif pin_name.startswith("bl") or pin_name.startswith("br"): pin_list = self.bitcell_array_inst.get_pins(pin_name) for pin in pin_list: - self.add_layout_pin_rect_center(text=pin_name, - layer=pin.layer, - offset=pin.center(), - width=pin.width(), - height=self.height) + self.add_layout_pin(text=pin_name, + layer=pin.layer, + offset=pin.ll().scale(1,0), + width=pin.width(), + height=self.height) - for index,(side1,side2) in enumerate([("bottop","left"),("topbot","right")]): - inst = getattr(self, "dummy_row_{}_inst".format(side1)) - pin_names = inst.mod.get_pin_names() - for pin_name in pin_names: - if pin_name.startswith("wl"): - pin_list = inst.get_pins(pin_name) - for pin in pin_list: - name = "replica_{0}_{1}".format(pin_name,index) - self.add_layout_pin_rect_center(text=name, - layer=pin.layer, - offset=pin.center(), - width=self.width, - height=pin.height()) + # Replica wordlines + for bit in range(self.left_rbl+self.right_rbl): + inst = self.replica_col_inst[bit] + for (pin_name,wl_name) in zip(self.cell.list_all_wl_names(),self.replica_wl_names_list[bit]): + # +1 for dummy row + pin_bit = bit+1 + # +row_size if above the array + if bit>=self.left_rbl: + pin_bit += self.row_size + + pin_name += "_{}".format(pin_bit) + pin = inst.get_pin(pin_name) + self.add_layout_pin(text=wl_name, + layer=pin.layer, + offset=pin.ll().scale(0,1), + width=self.width, + height=pin.height()) - # Replica columns - replica_sides = ["left"] - if self.num_ports==2: - replica_sides.append("right") - for index,side in enumerate(replica_sides): - inst = getattr(self, "replica_col_{}_inst".format(side)) - pin_names = inst.mod.get_pin_names() - for pin_name in pin_names: - if pin_name.startswith("bl") or pin_name.startswith("br"): - pin_list = inst.get_pins(pin_name) - for pin in pin_list: - name = "replica_{0}_{1}".format(pin_name,index) - self.add_layout_pin(text=name, - layer=pin.layer, - offset=pin.ll().scale(1,0), - width=pin.width(), - height=self.height) + # Replica bitlines + for bit in range(self.left_rbl+self.right_rbl): + inst = self.replica_col_inst[bit] + for (pin_name, bl_name) in zip(self.cell.list_all_bitline_names(),self.replica_bl_names_list[bit]): + pin = inst.get_pin(pin_name) + name = "replica_{0}_{1}".format(pin_name,bit) + self.add_layout_pin(text=name, + layer=pin.layer, + offset=pin.ll().scale(1,0), + width=pin.width(), + height=self.height) for pin_name in ["vdd","gnd"]: @@ -291,17 +337,17 @@ class replica_bitcell_array(design.design): # Non-pins - for side in ["botbot", "toptop"]: + for side in ["bot", "top"]: inst = getattr(self, "dummy_row_{}_inst".format(side)) pin_names = inst.mod.get_pin_names() for pin_name in pin_names: if pin_name.startswith("wl"): pin_list = inst.get_pins(pin_name) for pin in pin_list: - self.add_rect_center(layer=pin.layer, - offset=pin.center(), - width=self.width, - height=pin.height()) + self.add_rect(layer=pin.layer, + offset=pin.ll().scale(0,1), + width=self.width, + height=pin.height()) for side in ["left", "right"]: @@ -311,10 +357,10 @@ class replica_bitcell_array(design.design): if pin_name.startswith("b"): pin_list = inst.get_pins(pin_name) for pin in pin_list: - self.add_rect_center(layer=pin.layer, - offset=pin.center(), - width=pin.width(), - height=self.height) + self.add_rect(layer=pin.layer, + offset=pin.ll().scale(1,0), + width=pin.width(), + height=self.height) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index 2a441f02..61e540cc 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -48,53 +48,34 @@ class replica_bitline(design.design): #self.add_lvs_correspondence_points() # Extra pitch on top and right - self.width = self.replica_column_inst.rx() - self.delay_chain_inst.lx() + self.m2_pitch - self.height = max(self.replica_column_inst.uy(), self.delay_chain_inst.uy()) + self.m3_pitch + self.width = self.delay_chain_inst.rx() + self.m2_pitch + self.height = self.delay_chain_inst.uy() + self.m3_pitch self.add_boundary() self.DRC_LVS() def add_pins(self): - for pin in ["en", "out", "vdd", "gnd"]: - self.add_pin(pin) - + pin_list = ["en", "bl", "wl", "out", "vdd", "gnd"] + pin_dir = ["INPUT", "INPUT", "OUTPUT", "OUTPUT", "POWER", "GROUND"] + self.add_pin_list(pin_list, pin_dir) + def calculate_module_offsets(self): """ Calculate all the module offsets """ - # These aren't for instantiating, but we use them to get the dimensions - self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height) - - # Quadrant 1: Replica bitline and such are not rotated, but they must be placed far enough - # away from the delay chain/inverter with space for three M2 tracks - self.bitcell_offset = vector(0,self.replica_bitcell.height) - self.rbl_offset = self.bitcell_offset - - # Gap between the delay chain and RBL - gap_width = 2*self.m2_pitch - # Quadrant 4: with some space below it and tracks on the right for vdd/gnd - self.delay_chain_offset = vector(-self.delay_chain.width-gap_width,self.replica_bitcell.height) + self.delay_chain_offset = vector(0, self.inv.height) # Will be flipped vertically below the delay chain # Align it with the inverters in the delay chain to simplify supply connections self.rbl_inv_offset = self.delay_chain_offset + vector(2*self.inv.width, 0) # Placed next to the replica bitcell - self.access_tx_offset = vector(-gap_width-self.access_tx.width-self.inv.width, 0.5*self.inv.height) + self.access_tx_offset = vector(self.rbl_inv_offset.x + self.inv.width, 0.5*self.inv.height) def add_modules(self): """ Add the modules for later usage """ - self.replica_bitcell = factory.create(module_type="replica_bitcell") - self.add_mod(self.replica_bitcell) - - # This is the replica bitline load column that is the height of our array - self.rbl = factory.create(module_type="bitcell_array", - cols=1, - rows=self.bitcell_loads) - self.add_mod(self.rbl) - # FIXME: The FO and depth of this should be tuned self.delay_chain = factory.create(module_type="delay_chain", fanout_list=self.delay_fanout_list) @@ -125,35 +106,6 @@ class replica_bitline(design.design): mod=self.delay_chain) self.connect_inst(["en", "delayed_en", "vdd", "gnd"]) - self.replica_cell_inst=self.add_inst(name="bitcell", - mod=self.replica_bitcell) - temp = [] - for port in self.all_ports: - temp.append("bl{}_0".format(port)) - temp.append("br{}_0".format(port)) - for port in self.all_ports: - temp.append("delayed_en") - temp.append("vdd") - temp.append("gnd") - self.connect_inst(temp) - - self.replica_column_inst=self.add_inst(name="load", - mod=self.rbl) - - temp = [] - for port in self.all_ports: - temp.append("bl{}_0".format(port)) - temp.append("br{}_0".format(port)) - for wl in range(self.bitcell_loads): - for port in self.all_ports: - temp.append("gnd") - temp.append("vdd") - temp.append("gnd") - self.connect_inst(temp) - - self.wl_list = self.rbl.cell.list_all_wl_names() - self.bl_list = self.rbl.cell.list_all_bl_names() - def place_instances(self): """ Add all of the module instances in the logical netlist """ @@ -165,113 +117,23 @@ class replica_bitline(design.design): self.delay_chain_inst.place(self.delay_chain_offset) - self.replica_cell_inst.place(offset=self.bitcell_offset, - mirror="MX") - - self.replica_column_inst.place(self.rbl_offset) - def route(self): """ Connect all the signals together """ self.route_supplies() - self.route_wl() self.route_access_tx() - def route_wl(self): - """ Connect the RBL word lines to gnd """ - # Connect the WL and gnd pins directly to the center and right gnd rails - for row in range(self.bitcell_loads): - wl = self.wl_list[0]+"_{}".format(row) - pin = self.replica_column_inst.get_pin(wl) - - # Route the connection to the right so that it doesn't interfere with the cells - # Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions - pin_right = pin.rc() - pin_extension = pin_right + vector(self.m3_pitch,0) - - if pin.layer != "metal1": - continue - pin_width_ydir = pin.uy()-pin.by() - #Width is set to pin y width to avoid DRC issues with m1 gaps - self.add_path("metal1", [pin_right, pin_extension], pin_width_ydir) - self.add_power_pin("gnd", pin_extension) - - # for multiport, need to short wordlines to each other so they all connect to gnd. - wl_last = self.wl_list[-1]+"_{}".format(row) - pin_last = self.replica_column_inst.get_pin(wl_last) - self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0)) - - def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None): - """Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins.""" - #Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord. - #This is my (Hunter) first time editing layout in openram so this function is likely not optimal. - if len(self.all_ports) > 1: - #1. Create vertical metal for all the bitlines to connect to - #m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped - correct_y = vector(0, 0.5*drc("minwidth_metal1")) - #x spacing depends on the side being drawn. Unknown to me (Hunter) why the size of the space differs by the side. - #I assume this is related to how a wire is draw, but I have not investigated the issue. - if pin_side == "right": - correct_x = vector(0.5*drc("minwidth_metal1"), 0) - if offset_x_vec != None: - correct_x = offset_x_vec - else: - correct_x = vector(1.5*drc("minwidth_metal1"), 0) - - if wl_pin_a.uy() > wl_pin_b.uy(): - self.add_path("metal1", [wl_pin_a.rc()+correct_x+correct_y, wl_pin_b.rc()+correct_x-correct_y]) - else: - self.add_path("metal1", [wl_pin_a.rc()+correct_x-correct_y, wl_pin_b.rc()+correct_x+correct_y]) - elif pin_side == "left": - if offset_x_vec != None: - correct_x = offset_x_vec - else: - correct_x = vector(1.5*drc("minwidth_metal1"), 0) - - if wl_pin_a.uy() > wl_pin_b.uy(): - self.add_path("metal1", [wl_pin_a.lc()-correct_x+correct_y, wl_pin_b.lc()-correct_x-correct_y]) - else: - self.add_path("metal1", [wl_pin_a.lc()-correct_x-correct_y, wl_pin_b.lc()-correct_x+correct_y]) - else: - debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1) - - #2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this. - for port in self.all_ports: - if is_replica_cell: - wl = self.wl_list[port] - pin = self.replica_cell_inst.get_pin(wl) - else: - wl = self.wl_list[port]+"_{}".format(cell_row) - pin = self.replica_column_inst.get_pin(wl) - - if pin_side == "left": - self.add_path("metal1", [pin.lc()-correct_x, pin.lc()]) - elif pin_side == "right": - self.add_path("metal1", [pin.rc()+correct_x, pin.rc()]) - - - def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - # These are the instances that every bank has - top_instances = [self.replica_column_inst, - self.delay_chain_inst] - for inst in top_instances: - self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") - + self.copy_layout_pin(self.delay_chain_inst, "vdd") + self.copy_layout_pin(self.delay_chain_inst, "gnd") # Route the inverter supply pin from M1 # Only vdd is needed because gnd shares a rail with the delay chain pin = self.rbl_inv_inst.get_pin("vdd") self.add_power_pin("vdd", pin.lc()) - for pin in self.replica_cell_inst.get_pins("vdd"): - self.add_power_pin(name="vdd", loc=pin.center(), vertical=True, start_layer=pin.layer) - - for pin in self.replica_cell_inst.get_pins("gnd"): - self.add_power_pin("gnd", pin.center(), vertical=True, start_layer=pin.layer) @@ -304,24 +166,10 @@ class replica_bitline(design.design): # 3. Route the contact of previous route to the bitcell WL # route bend of previous net to bitcell WL - wl_offset = self.replica_cell_inst.get_pin(self.wl_list[0]).lc() - wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0) - wl_mid2 = vector(wl_mid1.x, contact_offset.y) - #xmid_point= 0.5*(wl_offset.x+contact_offset.x) - #wl_mid1 = vector(xmid_point,contact_offset.y) - #wl_mid2 = vector(xmid_point,wl_offset.y) - self.add_path("metal1", [wl_offset, wl_mid1, wl_mid2, contact_offset]) + self.add_layout_pin_rect_center(text="wl", + layer="metal1", + offset=contact_offset) - # 4. Short wodlines if multiport - wl = self.wl_list[0] - wl_last = self.wl_list[-1] - pin = self.replica_cell_inst.get_pin(wl) - pin_last = self.replica_cell_inst.get_pin(wl_last) - x_offset = self.short_wordlines(pin, pin_last, "left", True) - - #correct = vector(0.5*drc("minwidth_metal1"), 0) - #self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct]) - # DRAIN ROUTE # Route the drain to the vdd rail drain_offset = self.tx_inst.get_pin("D").center() @@ -334,13 +182,13 @@ class replica_bitline(design.design): self.add_path("metal1",[source_offset, inv_A_offset]) # Route the connection of the source route to the RBL bitline (left) - # Via will go halfway down from the bitcell - bl_offset = self.replica_cell_inst.get_pin(self.bl_list[0]).bc() - # Route down a pitch so we can use M2 routing - bl_down_offset = bl_offset - vector(0, self.m2_pitch) - self.add_path("metal2",[source_offset, bl_down_offset, bl_offset]) + source_down_offset = source_offset - vector(0,3*self.m2_pitch) + self.add_path("metal1",[source_offset, source_down_offset]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=source_offset) + self.add_layout_pin_rect_center(text="bl", + layer="metal1", + offset=source_down_offset) # BODY ROUTE # Connect it to the inverter well @@ -351,205 +199,6 @@ class replica_bitline(design.design): width=ur_offset.x-nwell_offset.x, height=ur_offset.y-nwell_offset.y) - def route_vdd(self): - """ Route all signals connected to vdd """ - - self.copy_layout_pin(self.delay_chain_inst,"vdd") - self.copy_layout_pin(self.replica_cell_inst,"vdd") - - # Connect the WL and vdd pins directly to the center and right vdd rails - # Connect RBL vdd pins to center and right rails - rbl_vdd_pins = self.replica_column_inst.get_pins("vdd") - for pin in rbl_vdd_pins: - if pin.layer != "metal1": - continue - start = vector(self.center_vdd_pin.cx(),pin.cy()) - end = vector(self.right_vdd_pin.cx(),pin.cy()) - self.add_layout_pin_segment_center(text="vdd", - layer="metal1", - start=start, - end=end) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end) - - # Add via for the inverter - pin = self.rbl_inv_inst.get_pin("vdd") - start = vector(self.left_vdd_pin.cx(),pin.cy()) - end = vector(self.center_vdd_pin.cx(),pin.cy()) - self.add_segment_center(layer="metal1", - start=start, - end=end) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start) - - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end) - - - # Add via for the RBC - pin = self.replica_cell_inst.get_pin("vdd") - start = pin.lc() - end = vector(self.right_vdd_pin.cx(),pin.cy()) - self.add_segment_center(layer="metal1", - start=start, - end=end) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end) - - # Create the RBL rails too - rbl_pins = self.replica_column_inst.get_pins("vdd") - for pin in rbl_pins: - if pin.layer != "metal1": - continue - # If above the delay line, route the full width - left = vector(self.left_vdd_pin.cx(),pin.cy()) - center = vector(self.center_vdd_pin.cx(),pin.cy()) - if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch: - start = left - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left) - else: - start = center - end = vector(self.right_vdd_pin.cx()+0.5*self.m1_width,pin.cy()) - self.add_layout_pin_segment_center(text="vdd", - layer="metal1", - start=start, - end=end) - - - - - - - def route_gnd(self): - """ Route all signals connected to gnd """ - - # Route the gnd lines from left to right - - # Add via for the delay chain - left_gnd_start = self.delay_chain_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) - left_gnd_end = vector(left_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch) - self.left_gnd_pin=self.add_segment_center(layer="metal2", - start=left_gnd_start, - end=left_gnd_end) - - # Gnd line to the left of the replica bitline - center_gnd_start = self.replica_cell_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) - center_gnd_end = vector(center_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch) - self.center_gnd_pin=self.add_segment_center(layer="metal2", - start=center_gnd_start, - end=center_gnd_end) - - # Gnd line to the right of the replica bitline - right_gnd_start = self.replica_cell_inst.lr().scale(1,0) + vector(self.m2_pitch,0) - right_gnd_end = vector(right_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch) - self.right_gnd_pin=self.add_segment_center(layer="metal2", - start=right_gnd_start, - end=right_gnd_end) - - - - # Connect the WL and gnd pins directly to the center and right gnd rails - for row in range(self.bitcell_loads): - wl = self.wl_list[0]+"_{}".format(row) - pin = self.replica_column_inst.get_pin(wl) - if pin.layer != "metal1": - continue - # If above the delay line, route the full width - left = vector(self.left_gnd_pin.cx(),pin.cy()) - center = vector(self.center_gnd_pin.cx(),pin.cy()) - if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch: - start = left - else: - start = center - end = vector(self.right_gnd_pin.cx(),pin.cy()) - self.add_layout_pin_segment_center(text="gnd", - layer="metal1", - start=start, - end=end) - if start == left: - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=center) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end) - - - # rbl_gnd_pins = self.replica_column_inst.get_pins("gnd") - # # Add L shapes to each vertical gnd rail - # for pin in rbl_gnd_pins: - # if pin.layer != "metal1": - # continue - # # If above the delay line, route the full width - # left = vector(self.left_gnd_pin.cx(),pin.cy()) - # center = vector(self.center_gnd_pin.cx(),pin.cy()) - # if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch: - # start = left - # else: - # start = center - # end = vector(self.right_gnd_pin.cx(),pin.cy()) - # self.add_segment_center(layer="metal1", - # start=start, - # end=end) - # if start == left: - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=left) - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=center) - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=end) - - - - # Connect the gnd pins of the delay chain to the left rails - dc_gnd_pins = self.delay_chain_inst.get_pins("gnd") - for pin in dc_gnd_pins: - if pin.layer != "metal1": - continue - start = vector(self.left_gnd_pin.cx(),pin.cy()) - # Note, we don't connect to the center rails because of - # via conflicts with the RBL - #end = vector(self.center_gnd_pin.cx(),pin.cy()) - end = pin.rc() - self.add_segment_center(layer="metal1", - start=start, - end=end) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start) - - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=end) - - - # Add via for the inverter - # pin = self.rbl_inv_inst.get_pin("gnd") - # start = vector(self.left_gnd_pin.cx(),pin.cy()) - # end = vector(self.center_gnd_pin.cx(),pin.cy()) - # self.add_segment_center(layer="metal1", - # start=start, - # end=end) - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=start) - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=end) - - - - # Create RBL rails too - rbl_pins = self.replica_column_inst.get_pins("gnd") - for pin in rbl_pins: - if pin.layer != "metal2": - continue - start = vector(pin.cx(),self.right_gnd_pin.by()) - end = vector(pin.cx(),self.right_gnd_pin.uy()) - self.add_layout_pin_segment_center(text="gnd", - layer="metal2", - start=start, - end=end) - def add_layout_pins(self): diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index d692db78..42903e7c 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -14,13 +14,25 @@ from globals import OPTS class replica_column(design.design): """ Generate a replica bitline column for the replica array. + Rows is the total number of rows i the main array. + Left_rbl and right_rbl are the number of left and right replica bitlines. + Replica bit specifies which replica column this is (to determine where to put the + replica cell. """ - def __init__(self, name, rows, num_ports): + def __init__(self, name, rows, left_rbl, right_rbl, replica_bit): design.design.__init__(self, name) - self.row_size = rows - self.num_ports = num_ports + self.rows = rows + self.left_rbl = left_rbl + self.right_rbl = right_rbl + self.replica_bit = replica_bit + # left, right, regular rows plus top/bottom dummy cells + self.total_size = self.left_rbl+rows+self.right_rbl+2 + + debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.") + debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1, + "Replica bit cannot be in the regular array.") self.create_netlist() if not OPTS.netlist_only: @@ -32,12 +44,11 @@ class replica_column(design.design): self.create_instances() def create_layout(self): - self.place_instances() - self.add_layout_pins() - - self.height = self.row_size*self.cell.height + self.height = self.total_size*self.cell.height self.width = self.cell.width + self.place_instances() + self.add_layout_pins() self.add_boundary() self.DRC_LVS() @@ -46,7 +57,7 @@ class replica_column(design.design): for cell_column in column_list: self.add_pin("{0}_{1}".format(cell_column,0)) row_list = self.cell.list_all_wl_names() - for row in range(self.row_size): + for row in range(self.total_size): for cell_row in row_list: self.add_pin("{0}_{1}".format(cell_row,row)) @@ -63,9 +74,15 @@ class replica_column(design.design): def create_instances(self): self.cell_inst = {} - for row in range(self.row_size): + for row in range(self.total_size): name="rbc_{0}".format(row) - if row>0 and rowleft_rbl and self.left_rbl and row