diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index b2697472..0102bf24 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -156,6 +156,16 @@ class bitcell(cell): self.storage_nets = storage_nets + self.wl_layer = "m1" + self.wl_dir = "H" + self.bl_layer = "m2" + self.bl_dir = "V" + + self.vdd_layer = "m1" + self.vdd_dir = "H" + self.gnd_layer = "m1" + self.gnd_dir = "H" + class cell_properties(): """ diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 6fca63c7..a37ccb2b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -673,9 +673,9 @@ class layout(): bot_pos = vector(x, bot_y) if num_pins==2: - self.add_layout_pin_rect_ends(name=name, - layer=pin_layer, - start=top_pos, + self.add_layout_pin_rect_ends(name=name, + layer=pin_layer, + start=top_pos, end=bot_pos, width=via_width) else: @@ -689,10 +689,10 @@ class layout(): def add_layout_pin_rect_ends(self, name, layer, start, end, width=None): # This adds pins on the end connected by a segment - top_rect = self.add_layout_pin_rect_center(text=name, + top_rect = self.add_layout_pin_rect_center(text=name, layer=layer, offset=start) - bot_rect = self.add_layout_pin_rect_center(text=name, + bot_rect = self.add_layout_pin_rect_center(text=name, layer=layer, offset=end) # This is made to not overlap with the pin above @@ -773,9 +773,9 @@ class layout(): right_pos = vector(right_x, y) if num_pins==2: - self.add_layout_pin_rect_ends(name=name, - layer=pin_layer, - start=left_pos, + self.add_layout_pin_rect_ends(name=name, + layer=pin_layer, + start=left_pos, end=right_pos, width=via_height) else: @@ -812,7 +812,6 @@ class layout(): end=end_pin.bc()) else: debug.error("Cannot have a point pin.", -1) - def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 438fd34a..27194a89 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -99,4 +99,4 @@ class col_cap_array(bitcell_base_array): inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.copy_power_pin(pin) + self.copy_layout_pin(inst, pin_name) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 62a7cfb6..70209767 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -145,6 +145,15 @@ class dff_buf_array(design.design): return dout_bar_name def route_supplies(self): + for row in range(self.rows): + vdd0_pin=self.dff_insts[row, 0].get_pin("vdd") + vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd") + self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height()) + + gnd0_pin=self.dff_insts[row, 0].get_pin("gnd") + gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd") + self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height()) + if OPTS.experimental_power and self.rows > 1: # Vertical straps on ends if multiple rows left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 5a7a046f..e1a3cbb8 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -6,7 +6,8 @@ import debug from bitcell_base_array import bitcell_base_array -from tech import drc, spice, cell_properties +from tech import drc, spice, preferred_directions +from tech import cell_properties as props from vector import vector from globals import OPTS from sram_factory import factory @@ -309,7 +310,7 @@ class replica_bitcell_array(bitcell_base_array): # row-based or column based power and ground lines. self.vertical_pitch = getattr(self, "{}_pitch".format(self.supply_stack[0])) self.horizontal_pitch = getattr(self, "{}_pitch".format(self.supply_stack[2])) - self.unused_offset = vector(2 * self.horizontal_pitch, 2 * self.vertical_pitch) + self.unused_offset = vector(0.25, 0.25) # This is a bitcell x bitcell offset to scale self.bitcell_offset = vector(self.cell.width, self.cell.height) @@ -461,38 +462,67 @@ class replica_bitcell_array(bitcell_base_array): def route_supplies(self): + bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) + + wl_layer = bitcell.wl_layer + wl_dir = bitcell.wl_dir + + bl_layer = bitcell.bl_layer + bl_dir = bitcell.bl_dir + + vdd_layer = bitcell.vdd_layer + vdd_dir = bitcell.vdd_dir + + gnd_layer = bitcell.gnd_layer + gnd_dir = bitcell.gnd_dir + # vdd/gnd are only connected in the perimeter cells - # replica column should only have a vdd/gnd in the dummy cell on top/bottom supply_insts = self.dummy_col_insts + self.dummy_row_insts - vdd_leftx = 0 - vdd_rightx = 0 - gnd_leftx = 0 - gnd_rightx = 0 + # For the wordlines + top_bot_mult = 1 + left_right_mult = 1 + + vdd_locs = [] + gnd_locs = [] + # There are always vertical pins for the WLs on the left/right if we have unused wordlines + self.left_gnd_locs = self.route_side_pin("gnd", "left", left_right_mult) + self.right_gnd_locs = self.route_side_pin("gnd","right", left_right_mult) + left_right_mult = 3 + + if gnd_dir == "V": + self.top_gnd_locs = self.route_side_pin("gnd", "top", top_bot_mult) + self.bot_gnd_locs = self.route_side_pin("gnd", "bot", top_bot_mult) + top_bot_mult = 3 + + if vdd_dir == "V": + self.top_vdd_locs = self.route_side_pin("vdd", "top", top_bot_mult) + self.bot_vdd_locs = self.route_side_pin("vdd", "bot", top_bot_mult) + elif vdd_dir == "H": + self.left_vdd_locs = self.route_side_pin("vdd", "left", left_right_mult) + self.right_vdd_locs = self.route_side_pin("vdd", "right", left_right_mult) + else: + debug.error("Invalid vdd direction {}".format(vdd_dir), -1) + for inst in supply_insts: for pin in inst.get_pins("vdd"): - (vdd_leftx, vdd_rightx) = self.connect_pin(pin, 2.5) + if vdd_dir == "V": + self.connect_side_pin(pin, "top", self.top_vdd_locs[0].y) + self.connect_side_pin(pin, "bot", self.bot_vdd_locs[0].y) + elif vdd_dir == "H": + self.connect_side_pin(pin, "left", self.left_vdd_locs[0].x) + self.connect_side_pin(pin, "right", self.right_vdd_locs[0].x) + + + for inst in supply_insts: for pin in inst.get_pins("gnd"): - (gnd_leftx, gnd_rightx) = self.connect_pin(pin) - - - self.add_layout_end_pin_segment_center(text="vdd", - layer=self.supply_stack[2], - start=vector(vdd_leftx, 0), - end=vector(vdd_leftx, self.height)) - self.add_layout_end_pin_segment_center(text="vdd", - layer=self.supply_stack[2], - start=vector(vdd_rightx, 0), - end=vector(vdd_rightx, self.height)) - self.add_layout_end_pin_segment_center(text="gnd", - layer=self.supply_stack[2], - start=vector(gnd_leftx, 0), - end=vector(gnd_leftx, self.height)) - self.add_layout_end_pin_segment_center(text="gnd", - layer=self.supply_stack[2], - start=vector(gnd_rightx, 0), - end=vector(gnd_rightx, self.height)) + if gnd_dir == "V": + self.connect_side_pin(pin, "top", self.top_gnd_locs[0].y) + self.connect_side_pin(pin, "bot", self.bot_gnd_locs[0].y) + elif gnd_dir == "H": + self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x) + self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x) def route_unused_wordlines(self): @@ -501,41 +531,120 @@ class replica_bitcell_array(bitcell_base_array): for inst in self.dummy_row_insts: for wl_name in self.col_cap_top.get_wordline_names(): pin = inst.get_pin(wl_name) - self.connect_pin(pin) + self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x) + self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x) # Ground the unused replica wordlines for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): if wl_name in self.gnd_wordline_names: pin = inst.get_pin(pin_name) - self.connect_pin(pin) + self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x) + self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x) + def route_side_pin(self, name, side, offset_multiple=1): + """ + Routes a vertical or horizontal pin on the side of the bbox. + The multiple specifies how many track offsets to be away from the side assuming + (0,0) (self.width, self.height) + """ + if side in ["left", "right"]: + return self.route_vertical_side_pin(name, side, offset_multiple) + elif side in ["top", "bottom", "bot"]: + return self.route_horizontal_side_pin(name, side, offset_multiple) + else: + debug.error("Invalid side {}".format(side), -1) - def connect_pin(self, pin, offset_multiple=1): + def route_vertical_side_pin(self, name, side, offset_multiple=1): + """ + Routes a vertical pin on the side of the bbox. + """ + if side == "left": + bot_loc = vector(-offset_multiple * self.vertical_pitch, 0) + top_loc = vector(-offset_multiple * self.vertical_pitch, self.height) + elif side == "right": + bot_loc = vector(self.width + offset_multiple * self.vertical_pitch, 0) + top_loc = vector(self.width + offset_multiple * self.vertical_pitch, self.height) - pin_layer = pin.layer + layer = self.supply_stack[2] + self.add_path(layer, [bot_loc, top_loc]) - left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) - right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) + self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=top_loc) + self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=bot_loc) + + return (bot_loc, top_loc) + + def route_horizontal_side_pin(self, name, side, offset_multiple=1): + """ + Routes a horizontal pin on the side of the bbox. + """ + if side in ["bottom", "bot"]: + left_loc = vector(0, -offset_multiple * self.horizontal_pitch) + right_loc = vector(self.width, -offset_multiple * self.horizontal_pitch) + elif side == "top": + left_loc = vector(0, self.height + offset_multiple * self.horizontal_pitch) + right_loc = vector(self.width, self.height + offset_multiple * self.horizontal_pitch) + + layer = self.supply_stack[0] + self.add_path(layer, [left_loc, right_loc]) + + self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=left_loc) + self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=right_loc) + + return (left_loc, right_loc) + + def connect_side_pin(self, pin, side, offset): + """ + Used to connect horizontal layers of pins to the left/right straps + locs provides the offsets of the pin strip end points. + """ + if side in ["left", "right"]: + self.connect_vertical_side_pin(pin, side, offset) + elif side in ["top", "bottom", "bot"]: + self.connect_horizontal_side_pin(pin, side, offset) + else: + debug.error("Invalid side {}".format(side), -1) + + def connect_horizontal_side_pin(self, pin, side, yoffset): + """ + Used to connect vertical layers of pins to the top/bottom horizontal straps + """ + cell_loc = pin.center() + pin_loc = vector(cell_loc.x, yoffset) # Place the pins a track outside of the array - left_loc = left_pin_loc - vector(offset_multiple * self.horizontal_pitch, 0) - right_loc = right_pin_loc + vector(offset_multiple * self.horizontal_pitch, 0) - self.add_via_stack_center(offset=left_loc, - from_layer=pin_layer, - to_layer=self.supply_stack[2], - directions=("H", "H")) - self.add_via_stack_center(offset=right_loc, - from_layer=pin_layer, + self.add_via_stack_center(offset=pin_loc, + from_layer=pin.layer, + to_layer=self.supply_stack[0], + directions=("V", "V")) + + # Add a path to connect to the array + self.add_path(pin.layer, [cell_loc, pin_loc]) + + + def connect_vertical_side_pin(self, pin, side, xoffset): + """ + Used to connect vertical layers of pins to the top/bottom vertical straps + """ + cell_loc = pin.center() + pin_loc = vector(xoffset, cell_loc.y) + + # Place the pins a track outside of the array + self.add_via_stack_center(offset=pin_loc, + from_layer=pin.layer, to_layer=self.supply_stack[2], directions=("H", "H")) # Add a path to connect to the array - self.add_path(pin_layer, [left_loc, right_loc]) - - return (left_loc.x, right_loc.x) - - + self.add_path(pin.layer, [cell_loc, pin_loc]) def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index e7dc9816..8f092dc4 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -106,10 +106,13 @@ class row_cap_array(bitcell_base_array): width=self.width, height=wl_pin.height()) - # Add vdd/gnd via stacks for row in range(1, self.row_size - 1): for col in range(self.column_size): inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.copy_power_pin(pin) + self.add_layout_pin(text=pin_name, + layer=pin.layer, + offset=pin.ll(), + width=pin.width(), + height=pin.height()) diff --git a/compiler/openram.py b/compiler/openram.py index 3a509f3b..8ce6ae1e 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -74,9 +74,8 @@ for path in output_files: from sram import sram -s = sram(name=OPTS.output_name, +s = sram(name=OPTS.output_name, sram_config=c) - # Output the files for the resulting SRAM s.save() diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index 9e35f558..b56c63db 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -14,4 +14,6 @@ tech_name = OPTS.tech_name nominal_corner_only = True check_lvsdrc = True +route_supplies = False + output_name = "sram" diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index d351ae36..7c1ac84b 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -31,7 +31,6 @@ tech_modules = module_type() # Custom cell properties ################################################### cell_properties = cell_properties() -cell_properties.bitcell_power_pin_directions = ("V", "V") ################################################### # Custom cell properties diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index a8c44996..525736a8 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -30,6 +30,8 @@ tech_modules = module_type() # Custom cell properties ################################################### cell_properties = cell_properties() +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "V" ################################################### # Custom cell properties diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index e81fe476..72250afc 100644 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -86,6 +86,13 @@ cell_properties.bitcell_1port.port_map = {'bl': 'BL', 'vpb': 'VPB', 'gnd': 'VGND'} +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.bl_layer = "m1" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" + cell_properties.bitcell_2port.mirror.x = True cell_properties.bitcell_2port.mirror.y = True cell_properties.bitcell_2port.end_caps = True @@ -98,6 +105,16 @@ cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', 'wl1': 'WL1', 'vdd': 'VDD', 'gnd': 'GND'} +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.vdd_layer = "m2" +cell_properties.bitcell_1port.vdd_dir = "H" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" +cell_properties.bitcell_2port.wl_layer = "m2" +cell_properties.bitcell_2port.vdd_layer = "m1" +cell_properties.bitcell_2port.vdd_dir = "H" +cell_properties.bitcell_2port.gnd_layer = "m2" +cell_properties.bitcell_2port.gnd_dir = "H" cell_properties.col_cap_1port_bitcell = cell(['br', 'vdd', 'gnd', 'bl', 'gate'], ['INPUT', 'POWER', 'GROUND', 'INPUT', 'INPUT'],