diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index acc25ff5..c9b139fd 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -635,7 +635,182 @@ class layout(lef.lef): self.add_via_center(layers=layer_stack, offset=bus_pos, rotate=90) + + def add_horizontal_trunk_route(self, pins, trunk_offset, + layer_stack=("metal1", "via1", "metal2"), + pitch=None): + """ + Create a trunk route for all pins with the the trunk located at the given y offset. + """ + if not pitch: + pitch = self.m1_pitch + + max_x = max([pin.center().x for pin in pins]) + min_x = min([pin.center().x for pin in pins]) + + half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])] + + # if we are less than a pitch, just create a non-preferred layer jog + if max_x-min_x < pitch: + # Add the horizontal trunk on the vertical layer! + self.add_path(layer_stack[2],[vector(min_x-half_minwidth,trunk_offset.y), vector(max_x+half_minwidth,trunk_offset.y)]) + + # Route each pin to the trunk + for pin in pins: + # No bend needed here + mid = vector(pin.center().x, trunk_offset.y) + self.add_path(layer_stack[2], [pin.center(), mid]) + else: + # Add the horizontal trunk + self.add_path(layer_stack[0],[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)]) + trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y) + + # Route each pin to the trunk + for pin in pins: + # Bend to the center of the trunk so it adds a via automatically + mid = vector(pin.center().x, trunk_offset.y) + self.add_wire(layer_stack, [pin.center(), mid, trunk_mid]) + + def add_vertical_trunk_route(self, pins, trunk_offset, + layer_stack=("metal1", "via1", "metal2"), + pitch=None): + """ + Create a trunk route for all pins with the the trunk located at the given x offset. + """ + if not pitch: + pitch = self.m2_pitch + max_y = max([pin.center().y for pin in pins]) + min_y = min([pin.center().y for pin in pins]) + + # Add the vertical trunk + half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])] + + # if we are less than a pitch, just create a non-preferred layer jog + if max_y-min_y < pitch: + # Add the horizontal trunk on the vertical layer! + self.add_path(layer_stack[0],[vector(trunk_offset.x,min_y-half_minwidth), vector(trunk_offset.x,max_y+half_minwidth)]) + + # Route each pin to the trunk + for pin in pins: + # No bend needed here + mid = vector(trunk_offset.x, pin.center().y) + self.add_path(layer_stack[0], [pin.center(), mid]) + else: + # Add the vertical trunk + self.add_path(layer_stack[2],[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)]) + trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),) + + # Route each pin to the trunk + for pin in pins: + # Bend to the center of the trunk so it adds a via automatically + mid = vector(trunk_offset.x, pin.center().y) + self.add_wire(layer_stack, [pin.center(), mid, trunk_mid]) + + + def create_channel_route(self, route_map, bottom_inst, top_inst, offset, + layer_stack=("metal1", "via1", "metal2"), pitch=None, + vertical=False): + """ + This is a simple channel route for one-to-one connections that + will jog the top route whenever there is a conflict. It does NOT + try to minimize the number of tracks -- instead, it picks an order to avoid the vertical + conflicts between pins. + """ + if not pitch and vertical: + pitch = self.m2_pitch + elif not pitch and not vertical: + pitch = self.m1_pitch + + + # FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the + # number of tracks! + # Initialize the vertical conflict graph (vcg) and make a list of all pins + vcg = {} + all_pins = {} + for (top_name, bot_name) in route_map: + vcg[top_name] = [] + vcg[bot_name] = [] + top_pin = top_inst.get_pin(top_name) + bot_pin = bottom_inst.get_pin(bot_name) + all_pins[top_name]=top_pin + all_pins[bot_name]=bot_pin + + + # Find the vertical pin conflicts + for (top_name, bot_name) in route_map: + top_pin = all_pins[top_name] + bot_pin = all_pins[bot_name] + if abs(top_pin.center().x-bot_pin.center().x) < pitch: + # The edges only go from top to bottom + # since we will order tracks bottom up + vcg[top_name].append(bot_name) + + # This is the starting offset of the first trunk + if vertical: + half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])] + offset = offset + vector(half_minwidth,0) + else: + half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])] + offset = offset + vector(0,half_minwidth) + + # list of routes to do + while vcg: + # get a route from conflict graph with empty fanout set + route_pin=None + for route_pin,conflicts in vcg.items(): + if len(conflicts)==0: + # Remove the pin from the keys + vcg.pop(route_pin,None) + # Remove the pin from all conflicts + # This is O(n^2), so maybe optimize it. + for pin,conflicts in vcg.items(): + if pin in conflicts: + conflicts.remove(route_pin) + vcg[pin]=conflicts + break + #print("Routing:",route_pin) + + # Get the connected pins from the routing map + for pin_connections in route_map: + if route_pin in pin_connections: + break + #print("Routing:",pin_connections) + + # Remove the other pins from the conflict graph too + for pin in pin_connections: + vcg.pop(pin,None) + + # Create a list of the pins rather than a list of the names + pin_list = [all_pins[pin_name] for pin_name in pin_connections] + + # Add the trunk route and move up to next track + if vertical: + self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch) + offset += vector(pitch,0) + else: + self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch) + offset += vector(0,pitch) + + + def create_vertical_channel_route(self, route_map, left_inst, right_inst, offset, + layer_stack=("metal1", "via1", "metal2"), + pitch=None): + """ + Wrapper to create a vertical channel route + """ + self.create_channel_route(route_map, left_inst, right_inst, offset, + layer_stack, pitch, vertical=True) + + def create_horizontal_channel_route(self, route_map, top_inst, bottom_inst, offset, + layer_stack=("metal1", "via1", "metal2"), + pitch=None): + """ + Wrapper to create a horizontal channel route + """ + self.create_channel_route(route_map, top_inst, bottom_inst, offset, + layer_stack, pitch, vertical=False) + def add_enclosure(self, insts, layer="nwell"): """ Add a layer that surrounds the given instances. Useful for creating wells, for example. Doesn't check for minimum widths or diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 17b6a3a2..9ca1df21 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -38,7 +38,7 @@ class sram_1bank(sram_base): control_pos.y + self.control_logic.height + self.m1_pitch) self.add_row_addr_dff(row_addr_pos) - data_gap = -self.m2_pitch*(self.word_size+1) + data_gap = -self.m1_pitch*(self.word_size+1) # Add the column address below the bank under the control # Keep it aligned with the data flops @@ -174,23 +174,14 @@ class sram_1bank(sram_base): def route_data_dff(self): """ Connect the output of the data flops to the write driver """ - # Create a horizontal bus - bus_names = ["data[{}]".format(x) for x in range(self.word_size)] - data_bus_offsets = self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.data_dff_inst.ul() + vector(0, self.m1_pitch), - names=bus_names, - length=self.data_dff_inst.width) - + # This is where the channel will start (y-dimension at least) + offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch) dff_names = ["dout[{}]".format(x) for x in range(self.word_size)] - data_dff_map = zip(dff_names, bus_names) - self.connect_horizontal_bus(data_dff_map, self.data_dff_inst, data_bus_offsets) - bank_names = ["bank_din[{}]".format(x) for x in range(self.word_size)] - data_bank_map = zip(bank_names, bus_names) - self.connect_horizontal_bus(data_bank_map, self.bank_inst, data_bus_offsets) + route_map = list(zip(bank_names, dff_names)) + self.create_horizontal_channel_route(route_map, self.data_dff_inst, self.bank_inst, offset)