def test_fully_connect_chips_pair_of_chips_no_wrap_around(self): """ Test that a model.fully_connect_chips connects (and leaves disconnected) the correct links when a pair of touching chips are created. """ # Test with the neighbouring chip being on every possible edge. for direction in [ topology.EAST , topology.NORTH_EAST , topology.NORTH , topology.WEST , topology.SOUTH_WEST , topology.SOUTH ]: other_chip_position = topology.to_xy(topology.add_direction((0,0,0), direction)) chips = { (0,0) : model.make_chip((0,0)) , other_chip_position : model.make_chip(other_chip_position) } model.fully_connect_chips(chips) # Only the touching ports are connected for port in model.Router.EXTERNAL_PORTS: if port == direction: # Touching port self.assertEqual( chips[(0,0)].router.connections[port] , chips[other_chip_position].router ) self.assertEqual( chips[other_chip_position].router.connections[topology.opposite(port)] , chips[(0,0)].router ) else: # Non-touching port self.assertIsNone(chips[(0,0)].router.connections[port]) self.assertIsNone(chips[other_chip_position].router.connections[topology.opposite(port)])
def test_connections(self): # Try with several sizes for torus_size in SpiNNakerTorusTests.TORUS_SIZES: self.generate_torus(*torus_size) # Get a dictionary of chips to their locations chips = {} for board in self.torus.boards.itervalues(): for chip in board.chips.itervalues(): chips[chip.get_mesh_position()] = chip # Check each chip is connected with their neighbours N, NE, W for pos, chip in chips.iteritems(): for direction in [topology.NORTH, topology.NORTH_EAST, topology.WEST]: other_pos = topology.to_xy( topology.add_direction(topology.zero_pad(pos), direction)) other_chip = chips[( other_pos[0]%(self.torus.width*12) , other_pos[1]%(self.torus.height*12) , )] other_direction = topology.opposite(direction) # Make sure we share links self.assertEqual(chip.get_in_link(direction), other_chip.get_out_link(other_direction)) self.assertEqual(chip.get_out_link(direction), other_chip.get_in_link(other_direction))
def test_fully_connect_chips_pair_of_chips_no_wrap_around(self): """ Test that a model.fully_connect_chips connects (and leaves disconnected) the correct links when a pair of touching chips are created. """ # Test with the neighbouring chip being on every possible edge. for direction in [ topology.EAST , topology.NORTH_EAST , topology.NORTH , topology.WEST , topology.SOUTH_WEST , topology.SOUTH ]: other_chip_position = topology.to_xy(topology.add_direction((0,0,0), direction)) chips = { (0,0) : model.make_chip((0,0)) , other_chip_position : model.make_chip(other_chip_position) } model.fully_connect_chips(chips) # Only the touching ports are connected for port in model.Router.EXTERNAL_PORTS: if port == direction: # Touching port self.assertEqual( chips[(0,0)][0].connections[port] , chips[other_chip_position][0] ) self.assertEqual( chips[other_chip_position][0].connections[topology.opposite(port)] , chips[(0,0)][0] ) else: # Non-touching port self.assertIsNone(chips[(0,0)][0].connections[port]) self.assertIsNone(chips[other_chip_position][0].connections[topology.opposite(port)])
def test_hexagon_edge_link(self): # Get the set of edge nodes for a 4-layer hexagon all_nodes = set(topology.hexagon(4)) inner_nodes = set(topology.hexagon(3)) outer_nodes = all_nodes - inner_nodes directions = [ topology.EAST, topology.NORTH_EAST, topology.NORTH, topology.WEST, topology.SOUTH_WEST, topology.SOUTH ] edges = [ topology.EDGE_TOP_LEFT, topology.EDGE_TOP, topology.EDGE_TOP_RIGHT, topology.EDGE_BOTTOM_RIGHT, topology.EDGE_BOTTOM, topology.EDGE_BOTTOM_LEFT, ] # Get the set of outward-facing links as (node_xy,direction) pairs outward_facing_links = [] for node in all_nodes: for direction in directions: # Get the node that this link would connect to facing_node = topology.to_xy( topology.add_direction(topology.zero_pad(node), direction)) # If that node isn't part of our set, it is an edge link if facing_node not in all_nodes: outward_facing_links.append((node, direction)) # Get the set of outward facing links according to the function under test all_links = [] for edge in edges: for num in range(8): all_links.append(topology.hexagon_edge_link(edge, num, 4)) # No duplicates self.assertEqual(len(all_links), len(set(all_links))) # The algorithm gets every outward facing edge self.assertEqual(set(all_links), set(outward_facing_links))
def fully_connect_chips(chips, wrap_around = False): """ Given a set of chips (i.e. (router, cores) tuples), fully interconnects the chips optionally including wrap-around links. """ # Calculate the bounds of the system's size (in case wrap_around is used) width = max(x for (x,y) in chips.iterkeys()) + 1 height = max(y for (x,y) in chips.iterkeys()) + 1 # Connect up the nodes to their neighbours for position, (router, cores) in chips.iteritems(): for direction in [topology.EAST, topology.NORTH_EAST, topology.NORTH]: next_x, next_y = topology.to_xy( topology.add_direction(topology.to_xyz(position), direction)) if wrap_around: next_x %= width next_y %= height # Only connect up to nodes which actually exist... if (next_x, next_y) in chips: router.connect(direction, chips[(next_x, next_y)][0], topology.opposite(direction))
def test_chips(self): # A board should have chips in the correct locations and no repeats self.assertEqual(len(self.board.chips), 48) self.assertEqual(set(self.board.chips.iterkeys()), set(topology.hexagon(4))) # For all inputs & outputs, if there is a chip in that direction it must be # connected via a SilistixLink and if not it should be attached to a # DeadLink. for src_pos, src_chip in self.board.chips.iteritems(): for direction in ( topology.EAST , topology.NORTH_EAST , topology.NORTH , topology.WEST , topology.SOUTH_WEST , topology.SOUTH ): in_link = src_chip.get_in_link(direction) out_link = src_chip.get_out_link(direction) dst_pos = topology.to_xy(topology.add_direction(topology.zero_pad(src_pos), direction)) if dst_pos in self.board.chips: # There is a chip opposite this connection dst_chip = self.board.chips[dst_pos] direction = topology.opposite(direction) # Check that they have the same link (connected to opposite ports) self.assertEqual(out_link, dst_chip.get_in_link(direction)) self.assertEqual(in_link, dst_chip.get_out_link(direction)) # And that they're SilistixLinks self.assertEqual(type(in_link), SilistixLink) self.assertEqual(type(out_link), SilistixLink) else: # No adjacent chip so should be DeadLinks self.assertEqual(type(in_link), DeadLink) self.assertEqual(type(out_link), DeadLink)
def test_threeboards(self): # Creating no threeboards makes no boards... self.assertEqual(list(topology.threeboards(0)), []) # Creating 2x2 threeboards (throw away the boards...) boards = [topology.to_xy(c) for c in topology.threeboards(2)] self.assertEqual(len(boards), 3*2*2) # Threeboard (0,0) self.assertTrue((0,0) in boards) self.assertTrue((0,1) in boards) self.assertTrue((1,1) in boards) # Threeboard (1,0) self.assertTrue((2,1) in boards) self.assertTrue((2,2) in boards) self.assertTrue((3,2) in boards) # Threeboard (0,1) self.assertTrue((-1,1) in boards) self.assertTrue((-1,2) in boards) self.assertTrue((0,2) in boards) # Threeboard (1,1) self.assertTrue((1,2) in boards) self.assertTrue((1,3) in boards) self.assertTrue((2,3) in boards)
def fully_connect_chips(chips, wrap_around=False): """ Given a set of chips (i.e. (router, cores) tuples), fully interconnects the chips optionally including wrap-around links. """ # Calculate the bounds of the system's size (in case wrap_around is used) width = max(x for (x, y) in chips.iterkeys()) + 1 height = max(y for (x, y) in chips.iterkeys()) + 1 # Connect up the nodes to their neighbours for position, (router, cores) in chips.iteritems(): for direction in [topology.EAST, topology.NORTH_EAST, topology.NORTH]: next_x, next_y = topology.to_xy( topology.add_direction(topology.to_xyz(position), direction)) if wrap_around: next_x %= width next_y %= height # Only connect up to nodes which actually exist... if (next_x, next_y) in chips: router.connect(direction, chips[(next_x, next_y)][0], topology.opposite(direction))
def test_to_xy(self): self.assertEqual(topology.to_xy((0,0,0)), (0,0)) self.assertEqual(topology.to_xy((1,1,1)), (0,0)) self.assertEqual(topology.to_xy((0,1,2)), (-2,-1)) self.assertEqual(topology.to_xy((-2,0,2)), (-4,-2))
def dimension_order_route(source, sinks, chips, use_wrap_around = False, dimension_order=(0,1,2)): """ Simple, naive dimension order routing optionally supporting wrap-around links. Note that when two DOR routes exist of equivalent length, one will be chosen at random. This routing algorithm does not attempt to route around dead links/cores and so some routes may fail in the presence of network errors. """ # Calculate the bounds of the system's size (in case wrap_around is used) width = max(x for (x,y) in chips.iterkeys()) + 1 height = max(y for (x,y) in chips.iterkeys()) + 1 node_sequences = [] unrouted_sinks = [] for sink in sinks: source_pos = model.core_to_router(source).position sink_pos = model.core_to_router(sink).position # Find the shortest vector from the source to the sink if use_wrap_around: vector = list(topology.to_torus_shortest_path(source_pos, sink_pos, (width,height))) else: vector = list(topology.to_shortest_path(topology.to_xyz(map( operator.sub , sink_pos , source_pos )))) node_sequence = [source, model.core_to_router(source)] # Route down each dimension in the given order for dimension in dimension_order: while vector[dimension] != 0: vx,vy = topology.to_xy((int(dimension == 0), int(dimension == 1), int(dimension == 2))) x, y = node_sequence[-1].position if vector[dimension] > 0: x += vx y += vy vector[dimension] -= 1 else: x -= vx y -= vy vector[dimension] += 1 x %= width y %= height node_sequence.append(chips[(x,y)][0]) # Add the sink node_sequence.append(sink) # Add the route if model.is_path_connected(node_sequence): node_sequences.append(node_sequence) else: unrouted_sinks.append(sink) return node_sequences, unrouted_sinks
def __init__( self , scheduler , system , link_send_cycles # SilistixLink , link_ack_cycles # SilistixLink , injection_buffer_length # SpiNNaker101 , router_period # SpiNNakerRouter , wait_before_emergency # SpiNNakerRouter , wait_before_drop # SpiNNakerRouter , core_period # SpiNNakerTrafficGenerator , packet_prob # SpiNNakerTrafficGenerator , distance_std = None # SpiNNakerTrafficGenerator ): """ link_send_cycles see SilistixLink link_ack_cycles see SilistixLink injection_buffer_length see SpiNNaker101 router_period see SpiNNakerRouter wait_before_emergency see SpiNNakerRouter wait_before_drop see SpiNNakerRouter core_period see SpiNNakerTrafficGenerator packet_prob see SpiNNakerTrafficGenerator distance_std see SpiNNakerTrafficGenerator """ self.scheduler = scheduler self.system = system # A dictionary { (x,y): SpiNNaker101, ... } of all contained chips. The # coordinates are relative to the central chip (created first in the # hexagon). self.chips = { } # Utility function to add a new chip to the chips dict. def add_chip(position): "Add a chip at the specified position" self.chips[position] = SpiNNaker101( self.scheduler , self.system , injection_buffer_length , router_period , wait_before_emergency , wait_before_drop , core_period , packet_prob , distance_std ) # Create the chips in a hexagonal pattern for position in topology.hexagon(4): add_chip(position) # Put SilistixLinks between them for src_pos, src_chip in self.chips.iteritems(): # Try and link this chip to all other neighbours which are towards the # top/right of the chip for direction in (topology.NORTH, topology.NORTH_EAST, topology.EAST): dst_pos = topology.to_xy( topology.add_direction(topology.zero_pad(src_pos), direction)) # If the chip exists, put links in this direction if dst_pos in self.chips: dst_chip = self.chips[dst_pos] in_link = SilistixLink(self.scheduler, link_send_cycles, link_ack_cycles) out_link = SilistixLink(self.scheduler, link_send_cycles, link_ack_cycles) src_chip.set_out_link(direction, out_link) src_chip.set_in_link(direction, in_link) dst_chip.set_in_link(topology.opposite(direction), out_link) dst_chip.set_out_link(topology.opposite(direction), in_link)