diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index cd4bbdc1..5ea5de78 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -12,6 +12,69 @@ from vector import vector import design +class channel_net(): + def __init__(self, net_name, pins, vertical): + self.name = net_name + self.pins = pins + self.vertical = vertical + + # Keep track of the internval + if vertical: + self.min_value = min(i.by() for i in pins) + self.max_value = max(i.uy() for i in pins) + else: + self.min_value = min(i.lx() for i in pins) + self.max_value = max(i.rx() for i in pins) + + # Keep track of the conflicts + self.conflicts = [] + + def __str__(self): + return self.name + + def __repr__(self): + return self.name + + def __lt__(self, other): + return self.min_value < other.min_value + + def vcg_pin_overlap(self, pin1, pin2, pitch): + """ Check for vertical or horizontal overlap of the two pins """ + + # FIXME: If the pins are not in a row, this may break. + # However, a top pin shouldn't overlap another top pin, + # for example, so the extra comparison *shouldn't* matter. + + # Pin 1 must be in the "BOTTOM" set + x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch + + # Pin 1 must be in the "LEFT" set + y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch + overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap) + return overlaps + + def vcg_nets_overlap(self, other, pitch): + """ + Check all the pin pairs on two nets and return a pin + overlap if any pin overlaps. + """ + + for pin1 in self.pins: + for pin2 in other.pins: + if self.vcg_pin_overlap(pin1, pin2, pitch): + return True + + return False + + def hcg_nets_overlap(self, other): + """ + Check if the horizontal span of the two nets overlaps eachother. + """ + min_overlap = self.min_value >= other.min_value and self.min_value <= other.max_value + max_overlap = self.max_value >= other.min_value and self.max_value <= other.max_value + return min_overlap or max_overlap + + class channel_route(design.design): unique_id = 0 @@ -86,104 +149,112 @@ class channel_route(design.design): # FIXME: This is O(n^2), so maybe optimize it. for other_pin, conflicts in g.items(): if pin in conflicts: - conflicts.remove(pin) - g[other_pin]=conflicts + g[other_pin].remove(pin) return g + + def route(self): + # Create names for the nets for the graphs + nets = [] + index = 0 + # print(self.netlist) + for pin_list in self.netlist: + nets.append(channel_net("n{}".format(index), pin_list, self.vertical)) + index += 1 - def vcg_nets_overlap(self, net1, net2): - """ - Check all the pin pairs on two nets and return a pin - overlap if any pin overlaps. - """ + # Create the (undirected) horizontal constraint graph + hcg = collections.OrderedDict() + for net1 in nets: + for net2 in nets: + if net1.name == net2.name: + continue + if net1.hcg_nets_overlap(net2): + try: + hcg[net1.name].append(net2.name) + except KeyError: + hcg[net1.name] = [net2.name] + try: + hcg[net2.name].append(net1.name) + except KeyError: + hcg[net2.name] = [net1.name] + + # Initialize the vertical conflict graph (vcg) + # and make a list of all pins + vcg = collections.OrderedDict() + + # print("Nets:") + # for net_name in nets: + # print(net_name, [x.name for x in nets[net_name]]) + + # Find the vertical pin conflicts + # FIXME: O(n^2) but who cares for now if self.vertical: pitch = self.horizontal_nonpref_pitch else: pitch = self.vertical_nonpref_pitch - for pin1 in net1: - for pin2 in net2: - if self.vcg_pin_overlap(pin1, pin2, pitch): - return True + for net in nets: + vcg[net.name] = [] - return False - - def route(self): - # FIXME: Must extend this to a horizontal conflict graph - # too if we want to minimize the - # number of tracks! - # hcg = {} - - # Initialize the vertical conflict graph (vcg) - # and make a list of all pins - vcg = collections.OrderedDict() - - # Create names for the nets for the graphs - nets = collections.OrderedDict() - index = 0 - # print(netlist) - for pin_list in self.netlist: - net_name = "n{}".format(index) - index += 1 - nets[net_name] = pin_list - - # print("Nets:") - # for net_name in nets: - # print(net_name, [x.name for x in nets[net_name]]) - - # Find the vertical pin conflicts - # FIXME: O(n^2) but who cares for now - for net_name1 in nets: - if net_name1 not in vcg.keys(): - vcg[net_name1] = [] - for net_name2 in nets: - if net_name2 not in vcg.keys(): - vcg[net_name2] = [] + for net1 in nets: + for net2 in nets: # Skip yourself - if net_name1 == net_name2: + if net1.name == net2.name: continue - if self.vcg_nets_overlap(nets[net_name1], - nets[net_name2]): - vcg[net_name2].append(net_name1) + + if net1.vcg_nets_overlap(net2, pitch): + vcg[net2.name].append(net1.name) current_offset = self.offset - - # list of routes to do - while vcg: + + # Sort nets by left edge value + nets.sort() + while len(nets) > 0: + + current_offset_value = current_offset.y if self.vertical else current_offset.x + # from pprint import pformat # print("VCG:\n", pformat(vcg)) + # for name,net in vcg.items(): + # print(name, net.min_value, net.max_value, net.conflicts) + # print(current_offset) # get a route from conflict graph with empty fanout set - net_name = None - for net_name, conflicts in vcg.items(): - if len(conflicts) == 0: - vcg = self.remove_net_from_graph(net_name, vcg) + for net in nets: + # If it has no conflicts and the interval is to the right of the current offset in the track + if net.min_value > current_offset_value and len(vcg[net.name]) == 0: + # print("Routing {}".format(net.name)) + # Add the trunk routes from the bottom up for + # horizontal or the left to right for vertical + if self.vertical: + self.add_vertical_trunk_route(net.pins, + current_offset, + self.vertical_nonpref_pitch) + current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch) + else: + self.add_horizontal_trunk_route(net.pins, + current_offset, + self.horizontal_nonpref_pitch) + current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y) + + # Remove the net from other constriants in the VCG + vcg = self.remove_net_from_graph(net.name, vcg) + nets.remove(net) + break else: - # FIXME: We don't support cyclic VCGs right now. - debug.error("Cyclic VCG in channel router.", -1) - - # These are the pins we'll have to connect - pin_list = nets[net_name] - # print("Routing:", net_name, [x.name for x in pin_list]) - - # Remove the net from other constriants in the VCG - vcg = self.remove_net_from_graph(net_name, vcg) - - # Add the trunk routes from the bottom up for - # horizontal or the left to right for vertical - if self.vertical: - self.add_vertical_trunk_route(pin_list, - current_offset, - self.vertical_nonpref_pitch) - # This accounts for the via-to-via spacings - current_offset += vector(self.horizontal_nonpref_pitch, 0) - else: - self.add_horizontal_trunk_route(pin_list, - current_offset, - self.horizontal_nonpref_pitch) - # This accounts for the via-to-via spacings - current_offset += vector(0, self.vertical_nonpref_pitch) + # If we made a full pass and the offset didn't change... + current_offset_value = current_offset.y if self.vertical else current_offset.x + initial_offset_value = self.offset.y if self.vertical else self.offset.x + if current_offset_value == initial_offset_value: + # FIXME: We don't support cyclic VCGs right now. + debug.error("Cyclic VCG in channel router.", -1) + # Increment the track and reset the offset to the start (like a typewriter) + if self.vertical: + current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, self.offset.y) + else: + current_offset = vector(self.offset.x, current_offset.y + self.vertical_nonpref_pitch) + # Return the size of the channel if self.vertical: self.width = 0 @@ -226,6 +297,17 @@ class channel_route(design.design): self.add_path(self.vertical_layer, [vector(min_x - half_layer_width, trunk_offset.y), vector(max_x + half_layer_width, trunk_offset.y)]) + + # Route each pin to the trunk + for pin in pins: + if pin.cy() < trunk_offset.y: + pin_pos = pin.uc() + else: + pin_pos = pin.bc() + + # No bend needed here + mid = vector(pin_pos.x, trunk_offset.y) + self.add_path(self.vertical_layer, [pin_pos, mid]) else: # Add the horizontal trunk self.add_path(self.horizontal_layer, @@ -269,6 +351,17 @@ class channel_route(design.design): self.add_path(self.horizontal_layer, [vector(trunk_offset.x, min_y - half_layer_width), vector(trunk_offset.x, max_y + half_layer_width)]) + + # Route each pin to the trunk + for pin in pins: + # Find the correct side of the pin + if pin.cx() < trunk_offset.x: + pin_pos = pin.rc() + else: + pin_pos = pin.lc() + # No bend needed here + mid = vector(trunk_offset.x, pin_pos.y) + self.add_path(self.horizontal_layer, [pin_pos, mid]) else: # Add the vertical trunk self.add_path(self.vertical_layer, @@ -292,18 +385,4 @@ class channel_route(design.design): to_layer=self.horizontal_layer, offset=pin_pos) - def vcg_pin_overlap(self, pin1, pin2, pitch): - """ Check for vertical or horizontal overlap of the two pins """ - - # FIXME: If the pins are not in a row, this may break. - # However, a top pin shouldn't overlap another top pin, - # for example, so the extra comparison *shouldn't* matter. - - # Pin 1 must be in the "BOTTOM" set - x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch - - # Pin 1 must be in the "LEFT" set - y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch - overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap) - return overlaps diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index a9731ed5..18f3414c 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -384,6 +384,14 @@ class sram_1bank(sram_base): bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, sram_pins))) + # spare wen dff + if self.num_spare_cols > 0 and port in self.write_ports: + dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] + dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] + bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + route_map.extend(list(zip(bank_pins, dff_pins))) + if self.num_wmasks > 0 and port in self.write_ports: layer_stack = self.m3_stack else: @@ -401,17 +409,17 @@ class sram_1bank(sram_base): offset=offset, layer_stack=layer_stack) - # Route these separately because sometimes the pin pitch on the write driver is too narrow for M3 (FreePDK45) - # spare wen dff - if self.num_spare_cols > 0 and port in self.write_ports: - dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] - dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] - bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] - bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - route_map = zip(bank_pins, dff_pins) - self.create_horizontal_channel_route(netlist=route_map, - offset=offset, - layer_stack=self.m1_stack) + # # Route these separately because sometimes the pin pitch on the write driver is too narrow for M3 (FreePDK45) + # # spare wen dff + # if self.num_spare_cols > 0 and port in self.write_ports: + # dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] + # dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] + # bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] + # bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + # route_map = zip(bank_pins, dff_pins) + # self.create_horizontal_channel_route(netlist=route_map, + # offset=offset, + # layer_stack=self.m1_stack) def route_clk(self): """ Route the clock network """