import logging
import os
import time
from pacman import exceptions
from pacman.model.graphs import AbstractSpiNNakerLinkVertex, AbstractFPGAVertex
from spinn_utilities.progress_bar import ProgressBar
logger = logging.getLogger(__name__)
[docs]def tag_allocator_report(report_folder, tag_infos):
""" Reports the tags that are being used by the tool chain for this\
simulation
:param report_folder: the folder to which the reports are being written
:param tag_infos: the tags container generated by the tools.
:rtype: None
"""
progress_bar = ProgressBar(
len(list(tag_infos.ip_tags)) + len(list(tag_infos.reverse_ip_tags)),
"Reporting Tags")
file_name = os.path.join(report_folder, "tags.rpt")
f_routing = None
try:
f_routing = open(file_name, "w")
except IOError:
logger.error("Generate_tag_report: Can't open file {} for "
"writing.".format(file_name))
for ip_tag in tag_infos.ip_tags:
f_routing.write("{}\n".format(ip_tag))
progress_bar.update()
for reverse_ip_tag in tag_infos.reverse_ip_tags:
f_routing.write("{}\n".format(reverse_ip_tag))
progress_bar.update()
f_routing.flush()
f_routing.close()
progress_bar.end()
[docs]def placer_reports_with_application_graph(
report_folder, hostname, graph, graph_mapper, placements, machine):
""" Reports that can be produced from placement given a application\
graph's existence
:param report_folder: the folder to which the reports are being written
:param hostname: the machine's hostname to which the placer worked on
:param graph: the application graph to which placements were built
:param graph_mapper: the mapping between application and machine \
graphs
:param placements: the placements objects built by the placer.
:param machine: the python machine object
:rtype: None
"""
placement_report_with_application_graph_by_vertex(
report_folder, hostname, graph, graph_mapper, placements)
placement_report_with_application_graph_by_core(
report_folder, hostname, placements, machine, graph_mapper)
sdram_usage_report_per_chip(
report_folder, hostname, placements, machine)
[docs]def placer_reports_without_application_graph(
report_folder, hostname, machine_graph, placements, machine):
"""
:param report_folder: the folder to which the reports are being written
:param hostname: the machine's hostname to which the placer worked on
:param placements: the placements objects built by the placer.
:param machine: the python machine object
:param machine_graph: the machine graph to which the reports are to\
operate on
:rtype: None
"""
placement_report_without_application_graph_by_vertex(
report_folder, hostname, placements, machine_graph)
placement_report_without_application_graph_by_core(
report_folder, hostname, placements, machine)
sdram_usage_report_per_chip(
report_folder, hostname, placements, machine)
[docs]def router_report_from_paths(
report_folder, routing_tables, routing_infos, hostname,
machine_graph, placements, machine):
""" Generates a text file of routing paths
:param routing_tables:
:param report_folder:
:param hostname:
:param routing_infos:
:param machine_graph:
:param placements:
:param machine:
:rtype: None
"""
file_name = os.path.join(report_folder, "edge_routing_info.rpt")
try:
with open(file_name, "w") as f:
progress = ProgressBar(machine_graph.n_outgoing_edge_partitions,
"Generating Routing path report")
f.write(" Edge Routing Report\n")
f.write(" ===================\n\n")
time_date_string = time.strftime("%c")
f.write("Generated: {}".format(time_date_string))
f.write(" for target machine '{}'".format(hostname))
f.write("\n\n")
for partition in progress.over(
machine_graph.outgoing_edge_partitions):
source_placement = placements.get_placement_of_vertex(
partition.pre_vertex)
key_and_mask = routing_infos.get_routing_info_from_partition(
partition).first_key_and_mask
for edge in partition.edges:
destination_placement = placements.get_placement_of_vertex(
edge.post_vertex)
path, number_of_entries = _search_route(
source_placement, destination_placement, key_and_mask,
routing_tables, machine)
text = ("**** Edge '{}', from vertex: '{}'"
" to vertex: '{}'".format(
edge.label, edge.pre_vertex.label,
edge.post_vertex.label))
text += " Takes path \n {}\n".format(path)
f.write(text)
f.write("Route length: {}\n".format(number_of_entries))
# End one entry:
f.write("\n")
except IOError:
logger.error("Generate_routing_reports: Can't open file {} for "
"writing.".format(file_name))
[docs]def partitioner_report(report_folder, hostname, graph, graph_mapper):
""" Generate report on the placement of vertices onto cores.
"""
# Cycle through all vertices, and for each cycle through its vertices.
# For each vertex, describe its core mapping.
file_name = os.path.join(report_folder, "partitioned_by_vertex.rpt")
try:
with open(file_name, "w") as f:
progress = ProgressBar(graph.n_vertices,
"Generating partitioner report")
f.write(" Placement Information by Vertex\n")
f.write(" ===============================\n\n")
time_date_string = time.strftime("%c")
f.write("Generated: {}".format(time_date_string))
f.write(" for target machine '{}'".format(hostname))
f.write("\n\n")
for v in progress.over(graph.vertices):
vertex_name = v.label
vertex_model = v.__class__.__name__
num_atoms = v.n_atoms
f.write("**** Vertex: '{}'\n".format(vertex_name))
f.write("Model: {}\n".format(vertex_model))
f.write("Pop size: {}\n".format(num_atoms))
f.write("Machine Vertices: \n")
machine_vertices = \
sorted(graph_mapper.get_machine_vertices(v),
key=lambda x: x.label)
machine_vertices = \
sorted(machine_vertices,
key=lambda x: graph_mapper.get_slice(x).lo_atom)
for sv in machine_vertices:
lo_atom = graph_mapper.get_slice(sv).lo_atom
hi_atom = graph_mapper.get_slice(sv).hi_atom
num_atoms = hi_atom - lo_atom + 1
my_string = " Slice {}:{} ({} atoms) \n"\
.format(lo_atom, hi_atom, num_atoms)
f.write(my_string)
f.write("\n")
except IOError:
logger.error("Generate_placement_reports: Can't open file {} for"
" writing.".format(file_name))
[docs]def placement_report_with_application_graph_by_vertex(
report_folder, hostname, graph, graph_mapper, placements):
""" Generate report on the placement of vertices onto cores by vertex.
:param report_folder: the folder to which the reports are being written
:param hostname: the machine's hostname to which the placer worked on
:param graph: the graph to which placements were built
:param graph_mapper: the mapping between graphs
:param placements: the placements objects built by the placer.
"""
# Cycle through all vertices, and for each cycle through its vertices.
# For each vertex, describe its core mapping.
file_name = os.path.join(report_folder, "placement_by_vertex.rpt")
try:
with open(file_name, "w") as f:
progress = ProgressBar(graph.n_vertices,
"Generating placement report")
f.write(" Placement Information by Vertex\n")
f.write(" ===============================\n\n")
time_date_string = time.strftime("%c")
f.write("Generated: {}".format(time_date_string))
f.write(" for target machine '{}'".format(hostname))
f.write("\n\n")
used_processors_by_chip = dict()
used_sdram_by_chip = dict()
vertex_by_processor = dict()
for v in progress.over(graph.vertices):
vertex_name = v.label
vertex_model = v.__class__.__name__
num_atoms = v.n_atoms
f.write("**** Vertex: '{}'\n".format(vertex_name))
f.write("Model: {}\n".format(vertex_model))
f.write("Pop size: {}\n".format(num_atoms))
f.write("Machine Vertices: \n")
machine_vertices = \
sorted(graph_mapper.get_machine_vertices(v),
key=lambda vert: vert.label)
machine_vertices = \
sorted(machine_vertices,
key=lambda vert:
graph_mapper.get_slice(vert).lo_atom)
for sv in machine_vertices:
lo_atom = graph_mapper.get_slice(sv).lo_atom
hi_atom = graph_mapper.get_slice(sv).hi_atom
num_atoms = hi_atom - lo_atom + 1
cur_placement = placements.get_placement_of_vertex(sv)
x, y, p = cur_placement.x, cur_placement.y, cur_placement.p
key = "{},{}".format(x, y)
if key in used_processors_by_chip:
used_pros = used_processors_by_chip[key]
else:
used_pros = list()
used_sdram_by_chip.update({key: 0})
vertex_by_processor["{},{},{}".format(x, y, p)] = sv
new_pro = [p, cur_placement]
used_pros.append(new_pro)
used_processors_by_chip.update({key: used_pros})
f.write(" Slice {}:{} ({} atoms) on core ({}, {}, {}) \n"
.format(lo_atom, hi_atom, num_atoms, x, y, p))
f.write("\n")
except IOError:
logger.error("Generate_placement_reports: Can't open file {} for"
" writing.".format(file_name))
[docs]def placement_report_without_application_graph_by_vertex(
report_folder, hostname, placements, machine_graph):
""" Generate report on the placement of vertices onto cores by vertex.
:param report_folder: the folder to which the reports are being written
:param hostname: the machine's hostname to which the placer worked on
:param placements: the placements objects built by the placer.
:param machine_graph: the machine graph generated by the end user
"""
# Cycle through all vertices, and for each cycle through its vertices.
# For each vertex, describe its core mapping.
file_name = os.path.join(report_folder, "placement_by_vertex.rpt")
try:
with open(file_name, "w") as f:
progress = ProgressBar(machine_graph.n_vertices,
"Generating placement report")
f.write(" Placement Information by Vertex\n")
f.write(" ===============================\n\n")
time_date_string = time.strftime("%c")
f.write("Generated: {}".format(time_date_string))
f.write(" for target machine '{}'".format(hostname))
f.write("\n\n")
used_processors_by_chip = dict()
used_sdram_by_chip = dict()
vertex_by_processor = dict()
for v in progress.over(machine_graph.vertices):
vertex_name = v.label
vertex_model = v.__class__.__name__
f.write("**** Vertex: '{}'\n".format(vertex_name))
f.write("Model: {}\n".format(vertex_model))
cur_placement = placements.get_placement_of_vertex(v)
x, y, p = cur_placement.x, cur_placement.y, cur_placement.p
key = "{},{}".format(x, y)
if key in used_processors_by_chip:
used_pros = used_processors_by_chip[key]
else:
used_pros = list()
used_sdram_by_chip.update({key: 0})
vertex_by_processor["{},{},{}".format(x, y, p)] = v
new_pro = [p, cur_placement]
used_pros.append(new_pro)
used_processors_by_chip.update({key: used_pros})
f.write(" Placed on core ({}, {}, {})\n\n".format(x, y, p))
except IOError:
logger.error("Generate_placement_reports: Can't open file {} for"
" writing.".format(file_name))
[docs]def placement_report_with_application_graph_by_core(
report_folder, hostname, placements, machine, graph_mapper):
""" Generate report on the placement of vertices onto cores by core.
:param report_folder: the folder to which the reports are being written
:param hostname: the machine's hostname to which the placer worked on
:param graph_mapper: the mapping between application and machine\
graphs
:param machine: the spinnaker machine object
:param placements: the placements objects built by the placer.
"""
# File 2: Placement by core.
# Cycle through all chips and by all cores within each chip.
# For each core, display what is held on it.
file_name = os.path.join(report_folder, "placement_by_core.rpt")
try:
with open(file_name, "w") as f:
progress = ProgressBar(machine.n_chips,
"Generating placement by core report")
f.write(" Placement Information by Core\n")
f.write(" =============================\n\n")
time_date_string = time.strftime("%c")
f.write("Generated: {}".format(time_date_string))
f.write(" for target machine '{}'".format(hostname))
f.write("\n\n")
for chip in progress.over(machine.chips):
written_header = False
for processor in chip.processors:
if placements.is_processor_occupied(
chip.x, chip.y, processor.processor_id):
if not written_header:
f.write("**** Chip: ({}, {})\n"
.format(chip.x, chip.y))
f.write("Application cores: {}\n"
.format(len(list(chip.processors))))
written_header = True
pro_id = processor.processor_id
vertex = placements.get_vertex_on_processor(
chip.x, chip.y, processor.processor_id)
app_vertex = graph_mapper.get_application_vertex(
vertex)
vertex_label = app_vertex.label
vertex_model = app_vertex.__class__.__name__
vertex_atoms = app_vertex.n_atoms
lo_atom = graph_mapper.get_slice(vertex).lo_atom
hi_atom = graph_mapper.get_slice(vertex).hi_atom
num_atoms = hi_atom - lo_atom + 1
f.write(" Processor {}: Vertex: '{}', pop size: {}\n"
.format(pro_id, vertex_label, vertex_atoms))
f.write(" "
"Slice on this core: {}:{} ({} atoms)\n"
.format(lo_atom, hi_atom, num_atoms))
f.write(" Model: {}\n\n".format(
vertex_model))
except IOError:
logger.error("Generate_placement_reports: Can't open file {} for "
"writing.".format(file_name))
[docs]def placement_report_without_application_graph_by_core(
report_folder, hostname, placements, machine):
""" Generate report on the placement of vertices onto cores by core.
:param report_folder: the folder to which the reports are being written
:param hostname: the machine's hostname to which the placer worked on
:param machine: the spinnaker machine object
:param placements: the placements objects built by the placer.
"""
# File 2: Placement by core.
# Cycle through all chips and by all cores within each chip.
# For each core, display what is held on it.
file_name = os.path.join(report_folder, "placement_by_core.rpt")
f = None
try:
with open(file_name, "w") as f:
progress = ProgressBar(machine.chips,
"Generating placement by core report")
f.write(" Placement Information by Core\n")
f.write(" =============================\n\n")
time_date_string = time.strftime("%c")
f.write("Generated: {}".format(time_date_string))
f.write(" for target machine '{}'".format(hostname))
f.write("\n\n")
for chip in progress.over(machine.chips):
written_header = False
for processor in chip.processors:
if placements.is_processor_occupied(
chip.x, chip.y, processor.processor_id):
if not written_header:
f.write("**** Chip: ({}, {})\n"
.format(chip.x, chip.y))
f.write("Application cores: {}\n"
.format(len(list(chip.processors))))
written_header = True
pro_id = processor.processor_id
vertex = placements.get_vertex_on_processor(
chip.x, chip.y, processor.processor_id)
f.write(" Processor {}: Vertex: '{}' \n"
.format(pro_id, vertex.label))
f.write(" Model: {}\n\n"
.format(vertex.__class__.__name__))
f.write("\n")
except IOError:
logger.error("Generate_placement_reports: Can't open file {} for "
"writing.".format(file_name))
[docs]def sdram_usage_report_per_chip(report_folder, hostname, placements, machine):
""" Reports the SDRAM used per chip
:param report_folder: the folder to which the reports are being written
:param hostname: the machine's hostname to which the placer worked on
:param placements: the placements objects built by the placer.
:param machine: the python machine object
:rtype: None
"""
file_name = os.path.join(report_folder, "chip_sdram_usage_by_core.rpt")
try:
with open(file_name, "w") as f:
f.write(" Memory Usage by Core\n")
f.write(" ====================\n\n")
time_date_string = time.strftime("%c")
f.write("Generated: %s" % time_date_string)
f.write(" for target machine '{}'".format(hostname))
f.write("\n\n")
used_sdram_by_chip = dict()
placements = sorted(placements.placements,
key=lambda x: x.vertex.label)
progress = ProgressBar(len(placements) + machine.n_chips,
"Generating SDRAM usage report")
for placement in placements:
reqs = placement.vertex.resources_required
x, y, p = placement.x, placement.y, placement.p
f.write("SDRAM reqs for core ({},{},{}) is {} KB\n".format(
x, y, p, int(reqs.sdram.get_value() / 1024.0)))
if (x, y) not in used_sdram_by_chip:
used_sdram_by_chip[(x, y)] = reqs.sdram.get_value()
else:
used_sdram_by_chip[(x, y)] += reqs.sdram.get_value()
progress.update()
for chip in machine.chips:
try:
used_sdram = used_sdram_by_chip[(chip.x, chip.y)]
if used_sdram != 0:
f.write(
"**** Chip: ({}, {}) has total memory usage of"
" {} KB ({} bytes) out of a max of "
"{} KB ({} bytes)\n\n".format(
chip.x, chip.y,
int(used_sdram / 1024.0),
used_sdram,
int(chip.sdram.size / 1024.0),
chip.sdram.size))
except KeyError:
# Do Nothing
pass
progress.update()
progress.end()
except IOError:
logger.error("Generate_placement_reports: Can't open file {} for "
"writing.".format(file_name))
[docs]def routing_info_report(report_folder, machine_graph, routing_infos):
""" Generates a report which says which keys is being allocated to each\
vertex
:param report_folder: the report folder to store this value
:param machine_graph:
:param routing_infos:
"""
file_name = os.path.join(
report_folder, "virtual_key_space_information_report.rpt")
try:
with open(file_name, "w") as f:
progress = ProgressBar(machine_graph.n_outgoing_edge_partitions,
"Generating Routing info report")
for vertex in machine_graph.vertices:
f.write("Vertex: {}\n".format(vertex))
for partition in machine_graph.\
get_outgoing_edge_partitions_starting_at_vertex(
vertex):
rinfo = routing_infos.get_routing_info_from_partition(
partition)
f.write(" Partition: {}, Routing Info: {}\n".format(
partition.identifier, rinfo.keys_and_masks))
progress.update()
progress.end()
except IOError:
logger.error("generate virtual key space information report: "
"Can't open file {} for writing.".format(file_name))
[docs]def router_report_from_router_tables(report_folder, routing_tables):
"""
:param report_folder:
:param routing_tables:
:rtype: None
"""
top_level_folder = os.path.join(report_folder, "routing_tables_generated")
if not os.path.exists(top_level_folder):
os.mkdir(top_level_folder)
progress = ProgressBar(routing_tables.routing_tables,
"Generating Router table report")
for routing_table in progress.over(routing_tables.routing_tables):
if routing_table.number_of_entries > 0:
_generate_routing_table(routing_table, top_level_folder)
[docs]def router_report_from_compressed_router_tables(report_folder, routing_tables):
"""
:param report_folder:
:param routing_tables:
:rtype: None
"""
top_level_folder = os.path.join(report_folder,
"compressed_routing_tables_generated")
if not os.path.exists(top_level_folder):
os.mkdir(top_level_folder)
progress = ProgressBar(routing_tables.routing_tables,
"Generating compressed router table report")
for routing_table in progress.over(routing_tables.routing_tables):
if routing_table.number_of_entries > 0:
_generate_routing_table(routing_table, top_level_folder)
def _generate_routing_table(routing_table, top_level_folder):
file_name = "routing_table_{}_{}.rpt".format(
routing_table.x, routing_table.y)
file_path = os.path.join(top_level_folder, file_name)
try:
with open(file_path, "w") as f:
f.write("Router contains {} entries\n".format(
routing_table.number_of_entries))
f.write("{: <5s} {: <10s} {: <10s} {: <10s} {: <7s} {}\n".format(
"Index", "Key", "Mask", "Route", "Default", "[Cores][Links]"))
f.write(
"{:-<5s} {:-<10s} {:-<10s} {:-<10s} {:-<7s} {:-<14s}\n".format(
"", "", "", "", "", ""))
line_format = "{: >5d} 0x{:08X} 0x{:08X} 0x{:08X} {: <7s} {}\n"
entry_count = 0
n_defaultable = 0
for entry in routing_table.multicast_routing_entries:
index = entry_count & 0xFFFF
key = entry.routing_entry_key
mask = entry.mask
route = _reduce_route_value(
entry.processor_ids, entry.link_ids)
route_txt = _expand_route_value(
entry.processor_ids, entry.link_ids)
entry_str = line_format.format(
index, key, mask, route, str(entry.defaultable), route_txt)
entry_count += 1
if entry.defaultable:
n_defaultable += 1
f.write(entry_str)
f.write("{} Defaultable entries\n".format(n_defaultable))
except IOError:
logger.error("Generate_placement_reports: Can't open file"
" {} for writing.".format(file_path))
[docs]def generate_comparison_router_report(
report_folder, routing_tables, compressed_routing_tables):
""" Make a report on comparison of the compressed and uncompressed \
routing tables
:param report_folder: the folder to store the resulting report
:param routing_tables: the original routing tables
:param compressed_routing_tables: the compressed routing tables
:rtype: None
"""
file_name = os.path.join(
report_folder, "comparison_of_compressed_uncompressed_routing_tables")
try:
with open(file_name, "w") as f:
progress = ProgressBar(
routing_tables.routing_tables,
"Generating comparison of router table report")
for table in progress.over(routing_tables.routing_tables):
x = table.x
y = table.y
compressed_table = compressed_routing_tables.\
get_routing_table_for_chip(x, y)
n_entries_uncompressed = table.number_of_entries
n_entries_compressed = compressed_table.number_of_entries
ratio = ((n_entries_uncompressed - n_entries_compressed) /
float(n_entries_uncompressed))
f.write(
"Uncompressed table at {}:{} has {} entries whereas "
"compressed table has {} entries. This is a decrease "
"of {} %\n".format(
x, y, n_entries_uncompressed, n_entries_compressed,
ratio * 100))
except IOError:
logger.error("Generate_router_comparison_reports: Can't open file"
" {} for writing.".format(file_name))
def _reduce_route_value(processors_ids, link_ids):
value = 0
for link in link_ids:
value += 1 << link
for processor in processors_ids:
value += 1 << (processor + 6)
return value
def _expand_route_value(processors_ids, link_ids):
""" Convert a 32-bit route word into a string which lists the target cores\
and links.
"""
# Convert processor targets to readable values:
route_string = "["
separator = ""
for processor in processors_ids:
route_string += "{}{}".format(separator, processor)
separator = ", "
route_string += "] ["
# Convert link targets to readable values:
link_labels = {0: 'E', 1: 'NE', 2: 'N', 3: 'W', 4: 'SW', 5: 'S'}
separator = ""
for link in link_ids:
route_string += "{}{}".format(separator, link_labels[link])
separator = ", "
route_string += "]"
return route_string
def _search_route(
source_placement, dest_placement, key_and_mask, routing_tables,
machine):
# Create text for starting point
source_vertex = source_placement.vertex
text = ""
if isinstance(source_vertex, AbstractSpiNNakerLinkVertex):
text += "Virtual SpiNNaker Link"
if isinstance(source_vertex, AbstractFPGAVertex):
text += "Virtual FPGA Link"
text += "{}:{}:{} -> ".format(
source_placement.x, source_placement.y, source_placement.p)
# Start the search
number_of_entries = 0
# If the destination is virtual, replace with the real destination chip
extra_text, total_number_of_entries = _recursive_trace_to_destinations(
source_placement.x, source_placement.y, key_and_mask,
dest_placement.x, dest_placement.y, dest_placement.p, machine,
routing_tables, number_of_entries)
text += extra_text
return text, total_number_of_entries
# locates the next dest position to check
def _recursive_trace_to_destinations(
chip_x, chip_y, key_and_mask,
dest_chip_x, dest_chip_y, dest_p, machine, routing_tables,
number_of_entries):
""" recursively search though routing tables till no more entries are\
registered with this key
"""
chip = machine.get_chip_at(chip_x, chip_y)
# If reached destination, return the core
if (chip_x == dest_chip_x and chip_y == dest_chip_y):
text = ""
if chip.virtual:
text += "Virtual "
text += "{}:{}:{}".format(dest_chip_x, dest_chip_y, dest_p)
return text, number_of_entries + 1
link_id = None
result = None
new_n_entries = None
if chip.virtual:
# If the current chip is virtual, use link out
link_id, link = next(iter(chip.router))
result, new_n_entries = _recursive_trace_to_destinations(
link.destination_x, link.destination_y, key_and_mask,
dest_chip_x, dest_chip_y, dest_p, machine,
routing_tables, number_of_entries)
else:
# If the current chip is real, find the link to the destination
table = routing_tables.get_routing_table_for_chip(chip_x, chip_y)
entry = _locate_routing_entry(table, key_and_mask.key)
for link_id in entry.link_ids:
link = chip.router.get_link(link_id)
result, new_n_entries = _recursive_trace_to_destinations(
link.destination_x, link.destination_y, key_and_mask,
dest_chip_x, dest_chip_y, dest_p, machine,
routing_tables, number_of_entries)
if result is not None:
break
if result is not None:
direction_text = _add_direction(link_id)
text = "{}:{}:{} -> {}".format(
chip_x, chip_y, direction_text, result)
return text, new_n_entries + 1
return None, None
def _add_direction(link):
# Convert link targets to readable values:
link_labels = {0: 'E', 1: 'NE', 2: 'N', 3: 'W', 4: 'SW', 5: 'S'}
return link_labels[link]
def _locate_routing_entry(current_router, key):
""" locate the entry from the router based off the edge
:param current_router: the current router being used in the trace
:param key: the key being used by the source placement
:return: the routing table entry
:raise PacmanRoutingException: when there is no entry located on this\
router.
"""
for entry in current_router.multicast_routing_entries:
if entry.mask & key == entry.routing_entry_key:
return entry
raise exceptions.PacmanRoutingException("no entry located")