def test_folded_torus(w, h, transformation, uncrinkle_direction, folds, mock_rhombus_to_rect, mock_fold): hex_boards, folded_boards = utils.folded_torus(w, h, transformation, uncrinkle_direction, folds) # Right number of boards produced assert len(hex_boards) == len(folded_boards) == 3 * w * h # Same board should be present in hexagonal layout as in folded layout assert set(b for (b, c) in hex_boards) == set(b for (b, c) in folded_boards) # Positions allocated should be unique assert len(set(c for (b, c) in hex_boards)) == len(hex_boards) assert len(set(c for (b, c) in folded_boards)) == len(folded_boards) # Should only use rhombus-to-rect when slicing if transformation == "slice": assert mock_rhombus_to_rect.called else: assert not mock_rhombus_to_rect.called min_x = min(c[0] for (b, c) in folded_boards) min_y = min(c[1] for (b, c) in folded_boards) # Should be based from 0,0 assert min_x == 0 assert min_y == 0 max_x = max(c[0] for (b, c) in folded_boards) max_y = max(c[1] for (b, c) in folded_boards) # Should be folded the required number of times assert mock_fold.call_args[0][1] == folds # Folded boards should fit within expected bounds (note that the 'or's here # are to allow for folding odd numbers of boards in each dimension). if transformation == "slice": if uncrinkle_direction == "rows": assert max_x == 2 * w or max_x + 1 == 2 * w assert max_y == int(1.5 * h) or max_y + 1 == int(1.5 * h) else: # if uncrinkle_direction == "columns": assert max_x == w or max_x + 1 == w assert max_y == 3 * h or max_y + 1 == 3 * h else: # if transformation == "shear" if uncrinkle_direction == "rows": assert max_x == 3 * w or max_x + 1 == 3 * w assert max_y == h or max_y + 1 == h else: # if uncrinkle_direction == "columns": assert max_x == w or max_x + 1 == w assert max_y == 3 * h or max_y + 1 == 3 * h
def test_folded_torus(w, h, transformation, uncrinkle_direction, folds, mock_rhombus_to_rect, mock_fold): hex_boards, folded_boards = utils.folded_torus(w, h, transformation, uncrinkle_direction, folds) # Right number of boards produced assert len(hex_boards) == len(folded_boards) == 3 * w * h # Same board should be present in hexagonal layout as in folded layout assert set(b for (b, c) in hex_boards) == set(b for (b, c) in folded_boards) # Positions allocated should be unique assert len(set(c for (b, c) in hex_boards)) == len(hex_boards) assert len(set(c for (b, c) in folded_boards)) == len(folded_boards) # Should only use rhombus-to-rect when slicing if transformation == "slice": assert mock_rhombus_to_rect.called else: assert not mock_rhombus_to_rect.called min_x = min(c[0] for (b, c) in folded_boards) min_y = min(c[1] for (b, c) in folded_boards) # Should be based from 0,0 assert min_x == 0 assert min_y == 0 max_x = max(c[0] for (b, c) in folded_boards) max_y = max(c[1] for (b, c) in folded_boards) # Should be folded the required number of times assert mock_fold.call_args[0][1] == folds # Folded boards should fit within expected bounds (note that the 'or's here # are to allow for folding odd numbers of boards in each dimension). if transformation == "slice": if uncrinkle_direction == "rows": assert max_x == 2*w or max_x + 1 == 2*w assert max_y == int(1.5*h) or max_y + 1 == int(1.5 * h) else: # if uncrinkle_direction == "columns": assert max_x == w or max_x + 1 == w assert max_y == 3 * h or max_y + 1 == 3 * h else: # if transformation == "shear" if uncrinkle_direction == "rows": assert max_x == 3*w or max_x + 1 == 3*w assert max_y == h or max_y + 1 == h else: # if uncrinkle_direction == "columns": assert max_x == w or max_x + 1 == w assert max_y == 3*h or max_y + 1 == 3*h
def cabinetised_boards(cabinet): from spinner import transforms from spinner import utils # Generate folded system hex_boards, folded_boards = utils.folded_torus(10, 8, "shear", "rows", (2, 2)) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, cabinet.frames_per_cabinet, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) return cabinetised_boards
def test_assign_wires(): # Simply check the appropriateness of each point provided in an example system. hex_boards, folded_boards = utils.folded_torus(4, 2, "shear", "rows", (2, 2)) cabinetised_boards = transforms.cabinetise(folded_boards, 1, 1, 24) wires = plan.enumerate_wires(cabinetised_boards) # Map each board to a point along a line. physical_boards = [(board, coordinates.Cartesian3D(float(b), 0.0, 0.0)) for board, (c, f, b) in cabinetised_boards] b2c = dict(physical_boards) # Given in no particular order available_wire_lengths = [8.0, 3.0, 24.0] board_wire_offset = { Direction.north: coordinates.Cartesian3D(0.0, 0.0, 0.0), Direction.south: coordinates.Cartesian3D(1.0, 0.0, 0.0), Direction.east: coordinates.Cartesian3D(0.0, 1.0, 0.0), Direction.west: coordinates.Cartesian3D(0.0, 0.0, 1.0), Direction.north_east: coordinates.Cartesian3D(1.0, 1.0, 1.0), Direction.south_west: coordinates.Cartesian3D(-1.0, -1.0, -1.0), } last_wire = None last_arc_height = None for src, dst, wire in plan.assign_wires(wires, physical_boards, board_wire_offset, available_wire_lengths, 0.0): # Check the wire was chosen correctly distance = ((b2c[src[0]] + board_wire_offset[src[1]]) - (b2c[dst[0]] + board_wire_offset[dst[1]])).magnitude() shortest_possible_wire, arc_height = metrics.physical_wire_length( distance, available_wire_lengths, 0.0) assert wire == shortest_possible_wire # Make sure wires are given in ascending order of arc height unless the # wire length changes if last_wire == wire and last_arc_height is not None: assert arc_height >= last_arc_height last_arc_height = arc_height last_wire = wire
def test_assign_wires(): # Simply check the appropriateness of each point provided in an example system. hex_boards, folded_boards = utils.folded_torus(4, 2, "shear", "rows", (2,2)) cabinetised_boards = transforms.cabinetise(folded_boards, 1, 1, 24) wires = plan.enumerate_wires(cabinetised_boards) # Map each board to a point along a line. physical_boards = [(board, coordinates.Cartesian3D(float(b), 0.0, 0.0)) for board, (c, f, b) in cabinetised_boards] b2c = dict(physical_boards) # Given in no particular order available_wire_lengths = [8.0, 3.0, 24.0] board_wire_offset = { Direction.north: coordinates.Cartesian3D(0.0, 0.0, 0.0), Direction.south: coordinates.Cartesian3D(1.0, 0.0, 0.0), Direction.east: coordinates.Cartesian3D(0.0, 1.0, 0.0), Direction.west: coordinates.Cartesian3D(0.0, 0.0, 1.0), Direction.north_east: coordinates.Cartesian3D(1.0, 1.0, 1.0), Direction.south_west: coordinates.Cartesian3D(-1.0, -1.0, -1.0), } last_wire = None last_arc_height = None for src, dst, wire in plan.assign_wires(wires, physical_boards, board_wire_offset, available_wire_lengths, 0.0): # Check the wire was chosen correctly distance = ((b2c[src[0]] + board_wire_offset[src[1]]) - (b2c[dst[0]] + board_wire_offset[dst[1]])).magnitude() shortest_possible_wire, arc_height = metrics.physical_wire_length( distance, available_wire_lengths, 0.0) assert wire == shortest_possible_wire # Make sure wires are given in ascending order of arc height unless the # wire length changes if last_wire == wire and last_arc_height is not None: assert arc_height >= last_arc_height last_arc_height = arc_height last_wire = wire
def main(args=None): parser = argparse.ArgumentParser( description="Generate visual maps from the SpiNNaker network topology to " "board locations.") arguments.add_version_args(parser) arguments.add_image_args(parser) arguments.add_topology_args(parser) arguments.add_cabinet_args(parser) # Process command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) cabinet, num_frames = arguments.get_cabinets_from_args(parser, args) aspect_ratio = get_machine_map_aspect_ratio(w, h) output_filename, file_type, image_width, image_height =\ arguments.get_image_from_args(parser, args, aspect_ratio) # Generate folded system hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) # Render the image Context = {"png": PNGContextManager, "pdf": PDFContextManager}[file_type] with Context(output_filename, image_width, image_height) as ctx: draw_machine_map(ctx, image_width, image_height, w, h, hex_boards, cabinetised_boards) return 0
def main(args=None): parser = argparse.ArgumentParser( description="Produce CSV listings of Ethernet connected chip physical and " "network positions.") arguments.add_version_args(parser) arguments.add_topology_args(parser) arguments.add_cabinet_args(parser) # Process command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) cabinet, num_frames = arguments.get_cabinets_from_args(parser, args) # Generate folded system hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) # Generate the output print("cabinet,frame,board,x,y") b2c = dict(cabinetised_boards) for board, hex_coord in sorted(hex_boards, key=(lambda v: topology.to_xy(v[1]))): x, y = topology.to_xy(topology.board_to_chip(hex_coord)) c, f, b = b2c[board] print(",".join(map(str, [c,f,b, x,y]))) return 0
def main(args=None): parser = argparse.ArgumentParser( description= "Produce CSV listings of Ethernet connected chip physical and " "network positions.") arguments.add_version_args(parser) arguments.add_topology_args(parser) arguments.add_cabinet_args(parser) # Process command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) cabinet, num_frames = arguments.get_cabinets_from_args(parser, args) # Generate folded system hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) # Generate the output print("cabinet,frame,board,x,y") b2c = dict(cabinetised_boards) for board, hex_coord in sorted(hex_boards, key=(lambda v: topology.to_xy(v[1]))): x, y = topology.to_xy(topology.board_to_chip(hex_coord)) c, f, b = b2c[board] print(",".join(map(str, [c, f, b, x, y]))) return 0
def main(args=None): parser = argparse.ArgumentParser( description= "Interactively guide the user through the process of wiring up a " "SpiNNaker machine.") arguments.add_version_args(parser) parser.add_argument("--no-tts", action="store_true", default=False, help="disable text-to-speech announcements of wiring " "steps") parser.add_argument("--no-auto-advance", action="store_true", default=False, help="disable auto-advancing through wiring steps") parser.add_argument("--fix", action="store_true", default=False, help="detect errors in existing wiring and just show " "corrective steps") parser.add_argument( "--log", type=str, metavar="LOGFILE", help="record the times at which each cable is installed") arguments.add_topology_args(parser) arguments.add_cabinet_args(parser) arguments.add_wire_length_args(parser) arguments.add_bmp_args(parser) arguments.add_proxy_args(parser) arguments.add_subset_args(parser) # Process command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) cabinet, num_frames = arguments.get_cabinets_from_args(parser, args) wire_lengths, min_slack = arguments.get_wire_lengths_from_args( parser, args, mandatory=True) bmp_ips = arguments.get_bmps_from_args(parser, args, cabinet.num_cabinets, num_frames) proxy_host_port = arguments.get_proxy_from_args(parser, args) wire_filter = arguments.get_subset_from_args(parser, args) if cabinet.num_cabinets == num_frames == 1: num_boards = 3 * w * h else: num_boards = cabinet.boards_per_frame # Generate folded system hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) physical_boards = transforms.cabinet_to_physical(cabinetised_boards, cabinet) # Focus on only the boards which are part of the system if cabinet.num_cabinets > 1: focus = [slice(0, cabinet.num_cabinets)] elif num_frames > 1: focus = [0, slice(0, num_frames)] else: focus = [0, 0, slice(0, w * h * 3)] # Generate wiring plan wires_between_boards, wires_between_frames, wires_between_cabinets =\ generate_wiring_plan(cabinetised_boards, physical_boards, cabinet.board_wire_offset, wire_lengths, min_slack) flat_wiring_plan = flatten_wiring_plan(wires_between_boards, wires_between_frames, wires_between_cabinets, cabinet.board_wire_offset) # Create a BMP connection/wiring probe or connect to a proxy if proxy_host_port is None: if len(bmp_ips) == 0: if args.fix: parser.error( "--fix requires that all BMPs be listed with --bmp") bmp_controller = None wiring_probe = None else: bmp_controller = BMPController(bmp_ips) # Create a wiring probe if bmp_controller is not None and (not args.no_auto_advance or args.fix): wiring_probe = WiringProbe(bmp_controller, cabinet.num_cabinets, num_frames, num_boards) else: # Fix is not supported since the proxy client does not recreate the # discover_wires method of WiringProbe. if args.fix: parser.error("--fix cannot be used with --proxy") # The proxy object provides a get_link_target and set_led method compatible # with those provided by bmp_controller and wiring_probe. Since these are # the only methods used, we use the proxy client object in place of # bmp_controller and wiring_probe. bmp_controller = wiring_probe = ProxyClient(*proxy_host_port) # Create a TimingLogger if required if args.log: if os.path.isfile(args.log): logfile = open(args.log, "a") add_header = False else: logfile = open(args.log, "w") add_header = True timing_logger = TimingLogger(logfile, add_header) else: logfile = None timing_logger = None # Convert wiring plan into cabinet coordinates b2c = dict(cabinetised_boards) wires = [] for ((src_board, src_direction), (dst_board, dst_direction), wire_length) \ in flat_wiring_plan: sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] wires.append(((sc, sf, sb, src_direction), (dc, df, db, dst_direction), wire_length)) # Filter wires according to user-specified rules wires = list(filter(wire_filter, wires)) if len(wires) == 0: parser.error("--subset selects no wires") if not args.fix: # If running normally, just run through the full set of wires wiring_plan = wires else: # If running in fix mode, generate a list of fixes to make correct_wires = set((src, dst) for src, dst, length in wires) actual_wires = set(wiring_probe.discover_wires()) to_remove = actual_wires - correct_wires to_add = correct_wires - actual_wires # Remove all bad wires first, then re-add good ones (note ordering now is # just reset to cabinets right-to-left, frames top-to-bottom and boards # left-to-right). wiring_plan = [(src, dst, None) for src, dst in sorted(to_remove)] for src, dst, length in wires: if (src, dst) in to_add: wiring_plan.append((src, dst, length)) if len(wiring_plan) == 0: print("No corrections required.") return 0 # Intialise the GUI and launch the mainloop ui = InteractiveWiringGuide(cabinet=cabinet, wire_lengths=wire_lengths, wires=wiring_plan, bmp_controller=bmp_controller, use_tts=not args.no_tts, focus=focus, wiring_probe=wiring_probe, auto_advance=not args.no_auto_advance, timing_logger=timing_logger) ui.mainloop() if logfile is not None: logfile.close() return 0
def main(args=None): parser = argparse.ArgumentParser( description="Generate illustrations of SpiNNaker machine wiring.") arguments.add_version_args(parser) arguments.add_image_args(parser) add_diagram_arguments(parser) arguments.add_topology_args(parser) arguments.add_cabinet_args(parser) arguments.add_subset_args(parser) # Process command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) cabinet, num_frames =\ arguments.get_cabinets_from_args(parser, args) aspect_ratio, focus, wire_thickness, highlights, hide_labels =\ get_diagram_arguments(parser, args, w, h, cabinet, num_frames) output_filename, file_type, image_width, image_height =\ arguments.get_image_from_args(parser, args, aspect_ratio) wire_filter = arguments.get_subset_from_args(parser, args) # Generate folded system hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) # Set up diagram md = MachineDiagram(cabinet) # Create lookup from cabinet coord to chip x/y to enable labelling of boards b2cab = dict(cabinetised_boards) b2chip = dict((b, topology.to_xy(topology.board_to_chip(c))) for b, c in hex_boards) cab2chip = dict((b2cab[b], b2chip[b]) for b, c in cabinetised_boards) # Add labels if not hide_labels: for cabinet_num in range(cabinet.num_cabinets): md.add_label(cabinet_num, cabinet_num) for frame_num in range(cabinet.frames_per_cabinet): md.add_label(frame_num, cabinet_num, frame_num) for board_num in range(cabinet.boards_per_frame): # Only label boards which are actually part of the system xy = cab2chip.get((cabinet_num, frame_num, board_num), None) if xy is not None: md.add_label("{} ({},{})".format(board_num, xy.x, xy.y), cabinet_num, frame_num, board_num) for socket in Direction: name = "".join(w[0] for w in socket.name.split("_")).upper() md.add_label(name, cabinet_num, frame_num, board_num, socket, rgba=(1.0, 1.0, 1.0, 0.7)) # Add highlights for highlight in highlights: md.add_highlight(*highlight, width=cabinet.board_dimensions.x/3.0) # Add wires wire_thickness_m = { "thick" : cabinet.board_dimensions.x / 5.0, "normal" : cabinet.board_dimensions.x / 10.0, "thin" : cabinet.board_dimensions.x / 20.0, }[wire_thickness] b2c = dict(cabinetised_boards) for direction in [Direction.north, Direction.west, Direction.north_east]: for b, c in cabinetised_boards: ob = b.follow_wire(direction) oc = b2c[ob] src = (c.cabinet, c.frame, c.board, direction) dst = (oc.cabinet, oc.frame, oc.board, direction.opposite) if wire_filter((src, dst)): md.add_wire(src, dst, width=wire_thickness_m) # Render the image Context = {"png": PNGContextManager, "pdf": PDFContextManager}[file_type] with Context(output_filename, image_width, image_height) as ctx: md.draw(ctx, image_width, image_height, *(((list(focus) + [None]*3)[:3])*2)) return 0
def test_partition_wires(): # Verify the correctness with a two-cabinet system. hex_boards, folded_boards = utils.folded_torus(10, 8, "shear", "rows", (2, 2)) cabinetised_boards = transforms.cabinetise(folded_boards, 2, 5, 24) all_wires = plan.enumerate_wires(cabinetised_boards) between_boards, between_frames, between_cabinets =\ plan.partition_wires(all_wires, cabinetised_boards) # Should have the correct set of categories assert len(between_boards) == 5 * 2 assert len(between_frames) == 2 seen_wires = set() b2c = dict(cabinetised_boards) for (cabinet, frame), wires in iteritems(between_boards): assert 0 <= cabinet < 2 assert 0 <= frame < 5 for ((src_board, src_direction), (dst_board, dst_direction)) in wires: # Check it really does stay in the same frame sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] assert sc == cabinet assert dc == cabinet assert sf == frame assert df == frame assert (sc, sf) == (dc, df) assert src_direction.opposite == dst_direction # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) for cabinet, wires in iteritems(between_frames): assert 0 <= cabinet < 2 for ((src_board, src_direction), (dst_board, dst_direction)) in wires: # Check it really does stay in the same cabinet sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] assert sc == cabinet assert dc == cabinet assert sc == dc assert sf != df assert src_direction.opposite == dst_direction # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) from pprint import pprint for ((src_board, src_direction), (dst_board, dst_direction)) in between_cabinets: # Check it really doesn't stay within a cabinet sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] assert sc != dc assert src_direction.opposite == dst_direction # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) # Should have seen all wires too! assert seen_wires == set(all_wires)
def test_folded_torus_bad_args(): with pytest.raises(TypeError): utils.folded_torus(1, 1, "foo", "rows", (1, 1)) with pytest.raises(TypeError): utils.folded_torus(1, 1, "slice", "foo", (1, 1))
def main(args=None): parser = argparse.ArgumentParser( description="Print basic wiring statistics for a specified " " configuration of boards.") arguments.add_version_args(parser) arguments.add_topology_args(parser) arguments.add_histogram_args(parser) arguments.add_wire_length_args(parser) arguments.add_cabinet_args(parser) # Process and display command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) histogram_bins = arguments.get_histogram_from_args(parser, args) wire_lengths, min_slack =\ arguments.get_wire_lengths_from_args(parser, args) cabinet, num_frames = arguments.get_cabinets_from_args(parser, args) print(heading("Wiring Statistics", 1)) print(heading("Folding Parameters", 2)) print(table([["Parameter", "Value", "Unit"], ["Number of boards", 3 * w * h, ""], ["System dimensions", "{}x{}".format(w, h), "triads"], ["Transformation", transformation, ""], ["Uncrinkle Direction", uncrinkle_direction, ""], ["Folds", "{}x{}".format(*folds), "pieces"], ["Number of cabinets", cabinet.num_cabinets, ""], ["Number of frames-per-cabinet", num_frames, ""], ["Number of boards-per-frame", cabinet.boards_per_frame, ""], ])) # Generate folded system and report wire-lengths after folding hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) print(heading("Non-cabinetised measurements", 2)) print(avg_wire_length_table(folded_boards, "boards")) # Divide into cabinets and report crossings cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) print(heading("Inter-cabinet/Inter-frame wiring", 2)) print(wire_counts_table(cabinetised_boards)) # Map to real, physical cabinets and measure wire lengths physical_boards = transforms.cabinet_to_physical(cabinetised_boards, cabinet) print(heading("Cabinetised measurements", 2)) print("All wire lengths described in this section do not include any slack.\n") print(avg_wire_length_table(physical_boards, "meters", cabinet.board_wire_offset)) # Generate a histogram of wire lengths print(heading("Wire length histogram", 2)) print("Wire lengths are selected which include at least {} meters " "of slack.\n".format(min_slack)) print(wire_length_table(physical_boards, wire_lengths if wire_lengths else histogram_bins, min_slack, cabinet.board_wire_offset)) return 0
def main(args=None): parser = argparse.ArgumentParser( description="Validate the wiring of a SpiNNaker system.") arguments.add_version_args(parser) parser.add_argument("--verbose", "-v", action="store_true", default=False, help="list all incorrect and missing wires") arguments.add_topology_args(parser) arguments.add_cabinet_args(parser) arguments.add_bmp_args(parser) # Process command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) cabinet, num_frames = arguments.get_cabinets_from_args(parser, args) bmp_ips = arguments.get_bmps_from_args(parser, args, cabinet.num_cabinets, num_frames) if len(bmp_ips) == 0: parser.error("BMP host names must be provided for every frame.") # Generate folded system hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) # Generate list of wires wires = plan.enumerate_wires(cabinetised_boards) # Set up the wiring probe bmp_controller = BMPController(bmp_ips) if cabinet.num_cabinets == 1 and num_frames == 1: num_boards = 3 * w * h else: num_boards = cabinet.boards_per_frame wiring_probe = probe.WiringProbe(bmp_controller, cabinet.num_cabinets, num_frames, num_boards) # Check for the presence of every wire missing = [] b2c = dict(cabinetised_boards) for ((src_board, src_direction), (dst_board, dst_direction)) in wires: src = tuple(list(b2c[src_board]) + [src_direction]) dst = tuple(list(b2c[dst_board]) + [dst_direction]) actual_dst = wiring_probe.get_link_target(*src) actual_src = wiring_probe.get_link_target(*dst) if actual_dst != dst or actual_src != src: missing.append((src, dst)) if missing: sys.stderr.write("{} wires missing or erroneously connected.\n".format( len(missing), len(wires))) if args.verbose: for src, dst in missing: print("C:{} F:{} B:{} {} <--> C:{} F:{} B:{} {}".format( src[0], src[1], src[2], src[3].name.replace("_", " "), dst[0], dst[1], dst[2], dst[3].name.replace("_", " "))) else: print("Add --verbose for a complete list.") return -1 else: sys.stderr.write("All {} wires correctly connected.\n".format(len(wires))) return 0
def main(args=None): parser = argparse.ArgumentParser( description= "Textually enumerate every connection required in a machine.") parser.add_argument("--sort-by", "-s", choices=["installation-order", "board", "wire-length"], default="board", help="Specifies the order the connections should be " "listed in the file: installation-order sorts in " "the most sensible order for installation, " "board lists wires on a board-by-board basis, " "wire-length lists in order of wire length.") arguments.add_version_args(parser) arguments.add_topology_args(parser) arguments.add_cabinet_args(parser) arguments.add_wire_length_args(parser) # Process command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) cabinet, num_frames = arguments.get_cabinets_from_args(parser, args) wire_lengths, min_slack = arguments.get_wire_lengths_from_args( parser, args, mandatory=True) # Generate folded system hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) physical_boards = transforms.cabinet_to_physical(cabinetised_boards, cabinet) # Generate wiring plan wires_between_boards, wires_between_frames, wires_between_cabinets =\ generate_wiring_plan(cabinetised_boards, physical_boards, cabinet.board_wire_offset, wire_lengths, min_slack) flat_wiring_plan = flatten_wiring_plan(wires_between_boards, wires_between_frames, wires_between_cabinets, cabinet.board_wire_offset) # Convert wiring plan into cabinet coordinates b2c = dict(cabinetised_boards) wires = [] for ((src_board, src_direction), (dst_board, dst_direction), wire_length) \ in flat_wiring_plan: sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] wires.append(((sc, sf, sb, src_direction), (dc, df, db, dst_direction), wire_length, src_board, dst_board)) b2p = dict(physical_boards) # Order as requested on the command-line if args.sort_by == "board": wires = sorted(wires) elif args.sort_by == "wire-length": wires = sorted(wires, key=(lambda w: (w[2], w[:2]))) elif args.sort_by == "installation-order": # pragma: no branch pass # List is initially in assembly order print("C F B Socket C F B Socket Length") print("-- -- -- ---------- -- -- -- ---------- ------") for ((sc, sf, sb, src_direction), (dc, df, db, dst_direction), wire_length, src_board, dst_board) in wires: print("{:2d} {:2d} {:2d} {:10s} {:2d} {:2d} {:2d} {:10s} {:0.2f}". format(sc, sf, sb, src_direction.name.replace("_", " "), dc, df, db, dst_direction.name.replace("_", " "), wire_length)) return 0
def test_generate_wiring_plan(): # Since generate_wiring_plan is largely a wrapper around the functions tested # above, this simply tests that the output is not insane... hex_boards, folded_boards = utils.folded_torus(10, 8, "shear", "rows", (2, 2)) cabinetised_boards = transforms.cabinetise(folded_boards, 2, 5, 24) cab = cabinet.Cabinet(**real) physical_boards = transforms.cabinet_to_physical(cabinetised_boards, cab) all_wires = plan.enumerate_wires(cabinetised_boards) available_wire_lengths = [0.3, 0.5, 1.0] b2c = dict(cabinetised_boards) between_boards, between_frames, between_cabinets =\ plan.generate_wiring_plan(cabinetised_boards, physical_boards, cab.board_wire_offset, available_wire_lengths, 0.0) seen_wires = set() assert set(between_boards) == set( (c, f, d) for c in range(cab.num_cabinets) for f in range(cab.frames_per_cabinet) for d in [Direction.north, Direction.south_west, Direction.east]) for (cabinet_num, frame_num, direction), wires in iteritems(between_boards): for ((src_board, src_direction), (dst_board, dst_direction), wire_length) in wires: # The board does stay in the frame_num and goes in the specified direction c, f, b = b2c[src_board] assert c == cabinet_num assert f == frame_num assert src_direction == direction c, f, b = b2c[dst_board] assert c == cabinet_num assert f == frame_num assert dst_direction == direction.opposite # The wire length chosen should exist assert wire_length in available_wire_lengths # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) assert set(between_frames) == set( (c, d) for c in range(cab.num_cabinets) for d in [Direction.north, Direction.south_west, Direction.east]) for (cabinet_num, direction), wires in iteritems(between_frames): for ((src_board, src_direction), (dst_board, dst_direction), wire_length) in wires: # The board does stay in the cabinet and goes in the specified direction c, f, b = b2c[src_board] assert c == cabinet_num assert src_direction == direction c, f, b = b2c[dst_board] assert c == cabinet_num assert dst_direction == direction.opposite assert wire_length in available_wire_lengths # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) assert set(between_cabinets) == set( [Direction.north, Direction.south_west, Direction.east]) for direction, wires in iteritems(between_cabinets): for ((src_board, src_direction), (dst_board, dst_direction), wire_length) in wires: # The board does stay in the cabinet and goes in the specified direction assert src_direction == direction assert dst_direction == direction.opposite assert wire_length in available_wire_lengths # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) # All wires should have been seen assert seen_wires == set(all_wires)
def main(args=None): parser = argparse.ArgumentParser( description="Interactively guide the user through the process of wiring up a " "SpiNNaker machine.") arguments.add_version_args(parser) parser.add_argument("--no-tts", action="store_true", default=False, help="disable text-to-speech announcements of wiring " "steps") parser.add_argument("--no-auto-advance", action="store_true", default=False, help="disable auto-advancing through wiring steps") parser.add_argument("--fix", action="store_true", default=False, help="detect errors in existing wiring and just show " "corrective steps") parser.add_argument("--log", type=str, metavar="LOGFILE", help="record the times at which each cable is installed") arguments.add_topology_args(parser) arguments.add_cabinet_args(parser) arguments.add_wire_length_args(parser) arguments.add_bmp_args(parser) arguments.add_proxy_args(parser) arguments.add_subset_args(parser) # Process command-line arguments args = parser.parse_args(args) (w, h), transformation, uncrinkle_direction, folds =\ arguments.get_topology_from_args(parser, args) cabinet, num_frames = arguments.get_cabinets_from_args(parser, args) wire_lengths, min_slack = arguments.get_wire_lengths_from_args( parser, args, mandatory=True) bmp_ips = arguments.get_bmps_from_args(parser, args, cabinet.num_cabinets, num_frames) proxy_host_port = arguments.get_proxy_from_args(parser, args) wire_filter = arguments.get_subset_from_args(parser, args) if cabinet.num_cabinets == num_frames == 1: num_boards = 3 * w * h else: num_boards = cabinet.boards_per_frame # Generate folded system hex_boards, folded_boards = folded_torus(w, h, transformation, uncrinkle_direction, folds) # Divide into cabinets cabinetised_boards = transforms.cabinetise(folded_boards, cabinet.num_cabinets, num_frames, cabinet.boards_per_frame) cabinetised_boards = transforms.remove_gaps(cabinetised_boards) physical_boards = transforms.cabinet_to_physical(cabinetised_boards, cabinet) # Focus on only the boards which are part of the system if cabinet.num_cabinets > 1: focus = [slice(0, cabinet.num_cabinets)] elif num_frames > 1: focus = [0, slice(0, num_frames)] else: focus = [0, 0, slice(0, w*h*3)] # Generate wiring plan wires_between_boards, wires_between_frames, wires_between_cabinets =\ generate_wiring_plan(cabinetised_boards, physical_boards, cabinet.board_wire_offset, wire_lengths, min_slack) flat_wiring_plan = flatten_wiring_plan(wires_between_boards, wires_between_frames, wires_between_cabinets, cabinet.board_wire_offset) # Create a BMP connection/wiring probe or connect to a proxy if proxy_host_port is None: if len(bmp_ips) == 0: if args.fix: parser.error("--fix requires that all BMPs be listed with --bmp") bmp_controller = None wiring_probe = None else: bmp_controller = BMPController(bmp_ips) # Create a wiring probe if bmp_controller is not None and (not args.no_auto_advance or args.fix): wiring_probe = WiringProbe(bmp_controller, cabinet.num_cabinets, num_frames, num_boards) else: # Fix is not supported since the proxy client does not recreate the # discover_wires method of WiringProbe. if args.fix: parser.error("--fix cannot be used with --proxy") # The proxy object provides a get_link_target and set_led method compatible # with those provided by bmp_controller and wiring_probe. Since these are # the only methods used, we use the proxy client object in place of # bmp_controller and wiring_probe. bmp_controller = wiring_probe = ProxyClient(*proxy_host_port) # Create a TimingLogger if required if args.log: if os.path.isfile(args.log): logfile = open(args.log, "a") add_header = False else: logfile = open(args.log, "w") add_header = True timing_logger = TimingLogger(logfile, add_header) else: logfile = None timing_logger = None # Convert wiring plan into cabinet coordinates b2c = dict(cabinetised_boards) wires = [] for ((src_board, src_direction), (dst_board, dst_direction), wire_length) \ in flat_wiring_plan: sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] wires.append(((sc, sf, sb, src_direction), (dc, df, db, dst_direction), wire_length)) # Filter wires according to user-specified rules wires = list(filter(wire_filter, wires)) if len(wires) == 0: parser.error("--subset selects no wires") if not args.fix: # If running normally, just run through the full set of wires wiring_plan = wires else: # If running in fix mode, generate a list of fixes to make correct_wires = set((src, dst) for src, dst, length in wires) actual_wires = set(wiring_probe.discover_wires()) to_remove = actual_wires - correct_wires to_add = correct_wires - actual_wires # Remove all bad wires first, then re-add good ones (note ordering now is # just reset to cabinets right-to-left, frames top-to-bottom and boards # left-to-right). wiring_plan = [(src, dst, None) for src, dst in sorted(to_remove)] for src, dst, length in wires: if (src, dst) in to_add: wiring_plan.append((src, dst, length)) if len(wiring_plan) == 0: print("No corrections required.") return 0 # Intialise the GUI and launch the mainloop ui = InteractiveWiringGuide(cabinet=cabinet, wire_lengths=wire_lengths, wires=wiring_plan, bmp_controller=bmp_controller, use_tts=not args.no_tts, focus=focus, wiring_probe=wiring_probe, auto_advance=not args.no_auto_advance, timing_logger=timing_logger) ui.mainloop() if logfile is not None: logfile.close() return 0
def test_partition_wires(): # Verify the correctness with a two-cabinet system. hex_boards, folded_boards = utils.folded_torus(10, 8, "shear", "rows", (2,2)) cabinetised_boards = transforms.cabinetise(folded_boards, 2, 5, 24) all_wires = plan.enumerate_wires(cabinetised_boards) between_boards, between_frames, between_cabinets =\ plan.partition_wires(all_wires, cabinetised_boards) # Should have the correct set of categories assert len(between_boards) == 5 * 2 assert len(between_frames) == 2 seen_wires = set() b2c = dict(cabinetised_boards) for (cabinet, frame), wires in iteritems(between_boards): assert 0 <= cabinet < 2 assert 0 <= frame < 5 for ((src_board, src_direction), (dst_board, dst_direction)) in wires: # Check it really does stay in the same frame sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] assert sc == cabinet assert dc == cabinet assert sf == frame assert df == frame assert (sc, sf) == (dc, df) assert src_direction.opposite == dst_direction # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) for cabinet, wires in iteritems(between_frames): assert 0 <= cabinet < 2 for ((src_board, src_direction), (dst_board, dst_direction)) in wires: # Check it really does stay in the same cabinet sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] assert sc == cabinet assert dc == cabinet assert sc == dc assert sf != df assert src_direction.opposite == dst_direction # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) from pprint import pprint for ((src_board, src_direction), (dst_board, dst_direction)) in between_cabinets: # Check it really doesn't stay within a cabinet sc, sf, sb = b2c[src_board] dc, df, db = b2c[dst_board] assert sc != dc assert src_direction.opposite == dst_direction # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) # Should have seen all wires too! assert seen_wires == set(all_wires)
def test_generate_wiring_plan(): # Since generate_wiring_plan is largely a wrapper around the functions tested # above, this simply tests that the output is not insane... hex_boards, folded_boards = utils.folded_torus(10, 8, "shear", "rows", (2,2)) cabinetised_boards = transforms.cabinetise(folded_boards, 2, 5, 24) cab = cabinet.Cabinet(**real) physical_boards = transforms.cabinet_to_physical(cabinetised_boards, cab) all_wires = plan.enumerate_wires(cabinetised_boards) available_wire_lengths = [0.3, 0.5, 1.0] b2c = dict(cabinetised_boards) between_boards, between_frames, between_cabinets =\ plan.generate_wiring_plan(cabinetised_boards, physical_boards, cab.board_wire_offset, available_wire_lengths, 0.0) seen_wires = set() assert set(between_boards) == set((c, f, d) for c in range(cab.num_cabinets) for f in range(cab.frames_per_cabinet) for d in [Direction.north, Direction.south_west, Direction.east]) for (cabinet_num, frame_num, direction), wires in iteritems(between_boards): for ((src_board, src_direction), (dst_board, dst_direction), wire_length) in wires: # The board does stay in the frame_num and goes in the specified direction c, f, b = b2c[src_board] assert c == cabinet_num assert f == frame_num assert src_direction == direction c, f, b = b2c[dst_board] assert c == cabinet_num assert f == frame_num assert dst_direction == direction.opposite # The wire length chosen should exist assert wire_length in available_wire_lengths # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) assert set(between_frames) == set((c, d) for c in range(cab.num_cabinets) for d in [Direction.north, Direction.south_west, Direction.east]) for (cabinet_num, direction), wires in iteritems(between_frames): for ((src_board, src_direction), (dst_board, dst_direction), wire_length) in wires: # The board does stay in the cabinet and goes in the specified direction c, f, b = b2c[src_board] assert c == cabinet_num assert src_direction == direction c, f, b = b2c[dst_board] assert c == cabinet_num assert dst_direction == direction.opposite assert wire_length in available_wire_lengths # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) assert set(between_cabinets) == set([Direction.north, Direction.south_west, Direction.east]) for direction, wires in iteritems(between_cabinets): for ((src_board, src_direction), (dst_board, dst_direction), wire_length) in wires: # The board does stay in the cabinet and goes in the specified direction assert src_direction == direction assert dst_direction == direction.opposite assert wire_length in available_wire_lengths # Check we've not seen it before wire = ((src_board, src_direction), (dst_board, dst_direction)) assert wire not in seen_wires seen_wires.add(wire) # All wires should have been seen assert seen_wires == set(all_wires)