From f0f8522ea5d88fdd2f82dd38b347c3094258b16a Mon Sep 17 00:00:00 2001 From: Andrew Rowley Date: Tue, 26 Sep 2023 08:36:53 +0100 Subject: [PATCH] Try more thorough approach --- link_test/link_test_vertex.py | 15 ++++- link_test/link_tester.py | 2 +- link_test/run_link_test.py | 106 +++++++++++++++++++++------------- link_test/src/link_test.c | 7 ++- 4 files changed, 85 insertions(+), 45 deletions(-) diff --git a/link_test/link_test_vertex.py b/link_test/link_test_vertex.py index 6a3e2d7b..579d3d1d 100644 --- a/link_test/link_test_vertex.py +++ b/link_test/link_test_vertex.py @@ -65,6 +65,10 @@ class ConfigData(LittleEndianStructure): # uint32_t receive_keys[N_LINKS]; ("receive_keys", c_uint32 * N_LINKS), + #: Masks to expect from neighbours + # uint32_t receive_masks[N_LINKS]; + ("receive_masks", c_uint32 * N_LINKS), + #: How many times to send per time step # uint32_t sends_per_timestep; ("sends_per_timestep", c_uint32), @@ -135,10 +139,12 @@ def generate_machine_data_specification( for i in range(N_LINKS): if self.__neighbours[i] is None: config[0].receive_keys[i] = 0xFFFFFFFF + config[0].receive_masks[i] = 0 else: - config[0].receive_keys[i] = \ - r_info.get_first_key_from_pre_vertex( - self.__neighbours[i], PARTITION_NAME) + info = r_info.get_routing_info_from_pre_vertex( + self.__neighbours[i], PARTITION_NAME) + config[0].receive_keys[i] = info.key + config[0].receive_masks[i] = info.mask config[0].sends_per_timestep = self.__sends_per_ts config[0].time_between_sends_us = int((ts / 2) / self.__sends_per_ts) config[0].packet_count_ok = ( @@ -203,3 +209,6 @@ def _n_additional_data_items(self): def check_failure(self): assert not self.__failed + + def get_n_keys_for_partition(self, partition_id): + return self.__sends_per_ts diff --git a/link_test/link_tester.py b/link_test/link_tester.py index 1d8bc954..8ba6461a 100644 --- a/link_test/link_tester.py +++ b/link_test/link_tester.py @@ -27,7 +27,7 @@ def run(): # Put link test on all chips chips = dict() for c_x, c_y in machine.chip_coordinates: - chips[c_x, c_y] = LinkTestVertex(c_x, c_y, 100, 1, run_time) + chips[c_x, c_y] = LinkTestVertex(c_x, c_y, 256, 0, run_time) front_end.add_machine_vertex_instance(chips[c_x, c_y]) # Connect links together diff --git a/link_test/run_link_test.py b/link_test/run_link_test.py index 14ab421f..cfe7d947 100644 --- a/link_test/run_link_test.py +++ b/link_test/run_link_test.py @@ -12,18 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -from time import sleep +import time import os import tempfile -import sys -import traceback +from shutil import rmtree import pytest -from spalloc.job import Job -from spalloc.states import JobState -import spinnaker_graph_front_end as front_end -from _pytest.outcomes import Skipped +from spinnman.spalloc.spalloc_client import SpallocClient +from spinnman.spalloc.spalloc_state import SpallocState from link_test.link_tester import run +SPALLOC_URL = "https://spinnaker.cs.man.ac.uk/spalloc" +SPALLOC_USERNAME = "jenkins" +SPALLOC_PASSWORD = os.getenv("SPALLOC_PASSWORD") +SPALLOC_MACHINE = "SpiNNaker1M" +WIDTH = 2 +HEIGHT = 2 + class LinkTest(object): @@ -31,41 +35,65 @@ def do_run(self): run() -boards = [(x, y, b) for x in range(20) for y in range(20) for b in range(3)] +boards = [(b_x, b_y) for b_x in range(0, 20, 2) for b_y in range(0, 20, 2)] +boards += [(b_x, b_y) for b_x in range(1, 20, 2) for b_y in range(1, 20, 2)] -@pytest.mark.parametrize("b_x,b_y,b_b", boards) -def test_run(b_x, b_y, b_b): - tmp_dir = os.path.abspath(os.path.join( - front_end.__path__[0], os.path.pardir, "link_test")) - job = Job(b_x, b_y, b_b, hostname="spinnaker.cs.man.ac.uk", - owner="Jenkins Link Test") - # Sleep before checking for queued in case of multiple jobs running - sleep(2.0) - if job.state == JobState.queued: - job.destroy("Queued") - pytest.skip(f"Board {b_x}, {b_y}, {b_b} is in use") - elif job.state == JobState.destroyed: - pytest.skip(f"Board {b_x}, {b_y}, {b_b} could not be allocated") +@pytest.mark.parametrize("x,y", boards) +def test_run(x, y): + test_dir = os.path.dirname(__file__) + client = SpallocClient(SPALLOC_URL, SPALLOC_USERNAME, SPALLOC_PASSWORD) + job = client.create_job_rect_at_board( + WIDTH, HEIGHT, triad=(x, y, 0), machine_name=SPALLOC_MACHINE) with job: - with tempfile.TemporaryDirectory( - prefix=f"{b_x}_{b_y}_{b_b}", dir=tmp_dir) as tmpdir: - os.chdir(tmpdir) - with open("spiNNakerGraphFrontEnd.cfg", "w", encoding="utf-8") as f: - f.write("[Machine]\n") - f.write("spalloc_server = None\n") - f.write(f"machine_name = {job.hostname}\n") - f.write("version = 5\n") - test = LinkTest() - test.do_run() + job.launch_keepalive_task() + # Wait for not queued for up to 30 seconds + time.sleep(1.0) + state = job.get_state(wait_for_change=True) + # If queued or destroyed skip test + if state == SpallocState.QUEUED: + job.destroy("Queued") + pytest.skip(f"Some boards starting at {x}, {y}, 0 are in use" + f" on job {job}") + elif state == SpallocState.DESTROYED: + pytest.skip( + f"Boards {x}, {y}, 0 could not be allocated on job {job}") + # Actually wait for ready now (as might be powering on) + job.wait_until_ready() + tmpdir = tempfile.mkdtemp(prefix=f"{x}_{y}_0", dir=test_dir) + os.chdir(tmpdir) + with open("spynnaker.cfg", "w", encoding="utf-8") as f: + f.write("[Machine]\n") + f.write("spalloc_server = None\n") + f.write(f"machine_name = {job.get_root_host()}\n") + f.write("version = 5\n") + f.write("\n") + f.write("[Reports]\n") + f.write("reports_enabled = False\n") + f.write("write_routing_table_reports = False\n") + f.write("write_routing_tables_from_machine_reports = False\n") + f.write("write_tag_allocation_reports = False\n") + f.write("write_algorithm_timings = False\n") + f.write("write_sdram_usage_report_per_chip = False\n") + f.write("write_partitioner_reports = False\n") + f.write("write_application_graph_placer_report = False\n") + f.write("write_redundant_packet_count_report = False\n") + f.write("write_data_speed_up_reports = False\n") + f.write("write_router_info_report = False\n") + f.write("write_network_specification_report = False\n") + f.write("write_provenance = False\n") + f.write("read_graph_provenance_data = False\n") + f.write("read_placements_provenance_data = False\n") + f.write("read_profile_data = False\n") + + test = LinkTest() + test.do_run() + + # If no errors we will get here and we can remove the tree; + # then only error folders will be left + rmtree(tmpdir) if __name__ == "__main__": - for x, y, b in boards: - print("", file=sys.stderr,) - print(f"*************** Testing {x}, {y}, {b} *******************", - file=sys.stderr) - try: - test_run(x, y, b) - except Skipped: - traceback.print_exc() + link_test = LinkTest() + link_test.do_run() diff --git a/link_test/src/link_test.c b/link_test/src/link_test.c index 9a6330ce..d84f0569 100644 --- a/link_test/src/link_test.c +++ b/link_test/src/link_test.c @@ -35,6 +35,9 @@ typedef struct { // Keys to expect from neighbours uint32_t receive_keys[N_LINKS]; + // Masks to expect from neighbours + uint32_t receive_masks[N_LINKS]; + // How many times to send per time step uint32_t sends_per_timestep; @@ -118,7 +121,7 @@ static p2p_data_t data_to_send; static void receive_data(uint key, uint payload) { uint32_t key_found = 0; for (uint32_t i = 0; i < N_LINKS; i++) { - if (key == config.receive_keys[i]) { + if ((key & config.receive_masks[i]) == config.receive_keys[i]) { packets_received[i]++; if (payload != expected_data[i]) { fails_received[i]++; @@ -184,7 +187,7 @@ static void send_data(UNUSED uint a, UNUSED uint b) { } for (uint32_t i = 0; i < config.sends_per_timestep; i++) { - spin1_send_mc_packet(config.send_key, data_to_send.data, 1); + spin1_send_mc_packet(config.send_key + i, data_to_send.data, 1); spin1_delay_us(config.time_between_sends_us); } }