diff --git a/spinnman/model/cpu_info.py b/spinnman/model/cpu_info.py index 101db2fd0..478a28ce5 100644 --- a/spinnman/model/cpu_info.py +++ b/spinnman/model/cpu_info.py @@ -329,3 +329,46 @@ def __str__(self): return "{}:{}:{:02n} ({:02n}) {:18} {:16s} {:3n}".format( self.x, self.y, self.p, self.physical_cpu_id, self._state.name, self._application_name, self._application_id) + + def get_status_string(self): + """ + Get a string indicating the status of the given core. + + :rtype: str + """ + if self.state == CPUState.RUN_TIME_EXCEPTION: + return ( + f"{self._x}:{self._y}:{self._p} " + f"(ph: {self._physical_cpu_id}) " + f"in state {self._state.name}:{self._run_time_error.name}\n" + f" r0={self._registers[0]}, r1={self._registers[1]}, " + f"r2={self._registers[2]}, r3={self._registers[3]}\n" + f" r4={self._registers[4]}, r5={self._registers[5]}, " + f"r6={self._registers[6]}, r7={self._registers[7]}\n" + f" PSR={self._processor_state_register}, " + f"SP={self._stack_pointer}, LR={self._link_register}\n") + else: + return ( + f"{self._x}:{self._y}:{self._p} in state {self._state.name}\n") + + @staticmethod + def mock_info(x, y, p, physical_cpu_id, state): + """ + Makes a CPU_info object for Testing purposes + + :param int x: + :param int y: + :param int p: + :param int physical_cpu_id: + :param CPUState CPIstate: + """ + registers = b'@\x00\x07\x08\xff\x00\x00\x00\x00\x00\x80\x00\xad\x00' \ + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ + b'\x00\x00\x00\x00\x00' + time = 1687857627 + application_name = b'scamp-3\x00\x00\x00\x00\x00\x00\x00\x00\x00' + iobuff_address = 197634 + cpu_data = ( + registers, 0, 0, 0, 0, physical_cpu_id, state.value, 0, 0, 0, 0, + 0, 0, 0, 0, time, application_name, iobuff_address, 0, 0, 0, 0, 0) + return CPUInfo(x, y, p, cpu_data) diff --git a/spinnman/model/cpu_infos.py b/spinnman/model/cpu_infos.py index 181d8cc51..9cf07ddbc 100644 --- a/spinnman/model/cpu_infos.py +++ b/spinnman/model/cpu_infos.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from spinnman.model.enums import CPUState - class CPUInfos(object): """ @@ -33,60 +31,25 @@ def add_info(self, cpu_info): """ self._cpu_infos[cpu_info.x, cpu_info.y, cpu_info.p] = cpu_info - def add_processor(self, x, y, processor_id, cpu_info): - """ - Add a info on a given core. - - :param int x: The x-coordinate of the chip - :param int y: The y-coordinate of the chip - :param int processor_id: A processor ID - :param CPUInfo cpu_info: - The CPU information for the core. - Not checked so could be None at test own risk + def add_infos(self, other, states): """ - self._cpu_infos[x, y, processor_id] = cpu_info + Adds all the infos in the other CPUInfos if the have one of the + required states - @property - def cpu_infos(self): - """ - The one per core core info. + mainly a support method for Transceiver.add_cpu_information_from_core - :return: iterable of x,y,p core info - :rtype: iterable(~spinnman.model.CPUInfo) + :param CPUInfos other: Another Infos object to merge in + :param list(~spinnman.model.enums.CPUState) states: + Only add if the Info has this state """ - return iter(self._cpu_infos.items()) + # pylint: disable=protected-access + for info in other._cpu_infos: + if info.state in states: + self.add_info(other) def __iter__(self): return iter(self._cpu_infos) - def iteritems(self): - """ - Get an iterable of (x, y, p), cpu_info. - :rtype: (iterable(tuple(int, int, int), ~spinnman.model.CPUInfo) - """ - return iter(self._cpu_infos.items()) - - def items(self): - return self._cpu_infos.items() - - def values(self): - return self._cpu_infos.values() - - def itervalues(self): - """ - Get an iterable of cpu_info. - """ - return iter(self._cpu_infos.items()) - - def keys(self): - return self._cpu_infos.keys() - - def iterkeys(self): - """ - Get an iterable of (x, y, p). - """ - return iter(self._cpu_infos.keys()) - def __len__(self): """ The total number of processors that are in these core subsets. @@ -128,24 +91,9 @@ def get_status_string(self): :param CPUInfos cpu_infos: A CPUInfos objects :rtype: str """ - break_down = "\n" - for (x, y, p), core_info in self.cpu_infos: - if core_info.state == CPUState.RUN_TIME_EXCEPTION: - break_down += " {}:{}:{} (ph: {}) in state {}:{}\n".format( - x, y, p, core_info.physical_cpu_id, core_info.state.name, - core_info.run_time_error.name) - break_down += " r0={}, r1={}, r2={}, r3={}\n".format( - core_info.registers[0], core_info.registers[1], - core_info.registers[2], core_info.registers[3]) - break_down += " r4={}, r5={}, r6={}, r7={}\n".format( - core_info.registers[4], core_info.registers[5], - core_info.registers[6], core_info.registers[7]) - break_down += " PSR={}, SP={}, LR={}\n".format( - core_info.processor_state_register, - core_info.stack_pointer, core_info.link_register) - else: - break_down += " {}:{}:{} in state {}\n".format( - x, y, p, core_info.state.name) + break_down = "" + for core_info in self._cpu_infos.values(): + break_down += core_info.get_status_string() return break_down def __str__(self): diff --git a/spinnman/transceiver/base_transceiver.py b/spinnman/transceiver/base_transceiver.py index b5c5f575c..ae018c1bb 100644 --- a/spinnman/transceiver/base_transceiver.py +++ b/spinnman/transceiver/base_transceiver.py @@ -738,12 +738,33 @@ def read_user(self, x, y, p, user): addr = self.__get_user_register_address_from_core(p, user) return self.read_word(x, y, addr) - @overrides(Transceiver.get_cpu_information_from_core) - def get_cpu_information_from_core(self, x, y, p): + @overrides(Transceiver.add_cpu_information_from_core) + def add_cpu_information_from_core(self, cpu_infos, x, y, p, states): core_subsets = CoreSubsets() core_subsets.add_processor(x, y, p) - cpu_infos = self.get_cpu_infos(core_subsets) - return cpu_infos.get_cpu_info(x, y, p) + new_infos = self.get_cpu_infos(core_subsets) + cpu_infos.add_infos(new_infos, states) + + def get_region_base_address(self, x, y, p): + """ + Gets the base address of the Region Table + + :param int x: The x-coordinate of the chip containing the processor + :param int y: The y-coordinate of the chip containing the processor + :param int p: The ID of the processor to get the address + :return: The adddress of the Region table for the selected core + :rtype: int + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If x, y, p is not a valid processor + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + return self.read_user(x, y, p, 0) @overrides(Transceiver.get_iobuf) def get_iobuf(self, core_subsets=None): @@ -980,6 +1001,8 @@ def wait_for_cores_to_be_in_state( if tries >= counts_between_full_check: cores_in_state = self.get_cpu_infos( all_core_subsets, cpu_states, True) + # convert to a list of xyp values + cores_in_state_xyps = list(cores_in_state) processors_ready = len(cores_in_state) tries = 0 @@ -989,7 +1012,7 @@ def wait_for_cores_to_be_in_state( for core_subset in all_core_subsets.core_subsets: for p in core_subset.processor_ids: if ((core_subset.x, core_subset.y, p) not in - cores_in_state.keys()): + cores_in_state_xyps): logger.warning( "waiting on {}:{}:{}", core_subset.x, core_subset.y, p) diff --git a/spinnman/transceiver/mockable_transceiver.py b/spinnman/transceiver/mockable_transceiver.py index 82c22ece0..d53e46c61 100644 --- a/spinnman/transceiver/mockable_transceiver.py +++ b/spinnman/transceiver/mockable_transceiver.py @@ -64,8 +64,12 @@ def get_clock_drift(self, x, y): def read_user(self, x, y, p, user): raise NotImplementedError("Needs to be mocked") - @overrides(Transceiver.get_cpu_information_from_core) - def get_cpu_information_from_core(self, x, y, p): + @overrides(Transceiver.add_cpu_information_from_core) + def add_cpu_information_from_core(self, cpu_infos, x, y, p, states): + raise NotImplementedError("Needs to be mocked") + + @overrides(Transceiver.get_region_base_address) + def get_region_base_address(self, x, y, p): raise NotImplementedError("Needs to be mocked") @overrides(Transceiver.get_iobuf) diff --git a/spinnman/transceiver/transceiver.py b/spinnman/transceiver/transceiver.py index f43cffd33..e53d20959 100644 --- a/spinnman/transceiver/transceiver.py +++ b/spinnman/transceiver/transceiver.py @@ -203,15 +203,18 @@ def read_user(self, x, y, p, user): # to .update_transaction_id_from_machine @abstractmethod - def get_cpu_information_from_core(self, x, y, p): + def add_cpu_information_from_core(self, cpu_infos, x, y, p, states): """ - Get information about a specific processor on the board. + Adds information about a specific processor on the board to the info + :param CPUInfo cpu_infos: Info to add data for this core to :param int x: The x-coordinate of the chip containing the processor :param int y: The y-coordinate of the chip containing the processor :param int p: The ID of the processor to get the information about + :param states: + If provided will only add the info if in one of the states + :type states: list(CPUState) :return: The CPU information for the selected core - :rtype: CPUInfo :raise SpinnmanIOException: If there is an error communicating with the board :raise SpinnmanInvalidPacketException: @@ -222,7 +225,28 @@ def get_cpu_information_from_core(self, x, y, p): :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - # see https://github.com/SpiNNakerManchester/SpiNNMan/pull/358 + # used by emergency_recover_state_from_failure + + @abstractmethod + def get_region_base_address(self, x, y, p): + """ + Gets the base address of the Region Table + + :param int x: The x-coordinate of the chip containing the processor + :param int y: The y-coordinate of the chip containing the processor + :param int p: The ID of the processor to get the address + :return: The adddress of the Region table for the selected core + :rtype: int + :raise SpinnmanIOException: + If there is an error communicating with the board + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If x, y, p is not a valid processor + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ @abstractmethod def get_iobuf(self, core_subsets=None): diff --git a/unittests/model_tests/test_cpu_infos.py b/unittests/model_tests/test_cpu_infos.py index 4d27ba050..465c411a0 100644 --- a/unittests/model_tests/test_cpu_infos.py +++ b/unittests/model_tests/test_cpu_infos.py @@ -23,30 +23,20 @@ class TestCpuInfos(unittest.TestCase): def setUp(self): unittest_setup() - def make_info_data(self, physical_cpu_id, state): - registers = b'@\x00\x07\x08\xff\x00\x00\x00\x00\x00\x80\x00\xad\x00' \ - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ - b'\x00\x00\x00\x00\x00' - time = 1687857627 - application_name = b'scamp-3\x00\x00\x00\x00\x00\x00\x00\x00\x00' - iobuff_address = 197634 - return (registers, 0, 0, 0, 0, physical_cpu_id, state.value, 0, 0, 0, - 0, 0, 0, 0, 0, time, application_name, iobuff_address, 0, 0, - 0, 0, 0) - def test_cpu_infos(self): infos = CPUInfos() - info = CPUInfo(0, 0, 1, self.make_info_data(5, CPUState.RUNNING)) - infos.add_info(info) - info = CPUInfo(0, 0, 2, self.make_info_data(6, CPUState.FINISHED)) - infos.add_info(info) - info = CPUInfo(1, 0, 1, self.make_info_data(7, CPUState.FINISHED)) - infos.add_info(info) + infos.add_info(CPUInfo.mock_info(0, 0, 1, 5, CPUState.RUNNING)) + infos.add_info(CPUInfo.mock_info(0, 0, 2, 6, CPUState.FINISHED)) + infos.add_info(CPUInfo.mock_info(1, 0, 1, 7, CPUState.FINISHED)) + infos.add_info(CPUInfo.mock_info( + 1, 1, 2, 4, CPUState.RUN_TIME_EXCEPTION)) + self.assertSetEqual(set(infos), + {(1, 0, 1), (0, 0, 1), (0, 0, 2), (1, 1, 2)}) self.assertEqual( - "['0, 0, 1 (ph: 5)', '0, 0, 2 (ph: 6)', '1, 0, 1 (ph: 7)']", - str(infos)) + "['0, 0, 1 (ph: 5)', '0, 0, 2 (ph: 6)', '1, 0, 1 (ph: 7)'," + " '1, 1, 2 (ph: 4)']", str(infos)) finished = infos.infos_for_state(CPUState.FINISHED) self.assertEqual( @@ -60,6 +50,16 @@ def test_cpu_infos(self): self.assertEqual( "0:0:02 (06) FINISHED scamp-3 0", str(info)) + # the str is for example purpose and may change without notice + self.assertEqual(infos.get_status_string(), + "0:0:1 in state RUNNING\n" + "0:0:2 in state FINISHED\n" + "1:0:1 in state FINISHED\n" + "1:1:2 (ph: 4) in state RUN_TIME_EXCEPTION:NONE\n" + " r0=134676544, r1=255, r2=8388608, r3=173\n" + " r4=0, r5=0, r6=0, r7=0\n" + " PSR=0, SP=0, LR=0\n") + if __name__ == '__main__': unittest.main()